feat: improve code
This commit is contained in:
parent
24d5d3cfad
commit
20c839bfa9
@ -80,6 +80,12 @@ const schema: ISchema = {
|
|||||||
// accept: 'jpg,png'
|
// accept: 'jpg,png'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showLogoOnly: {
|
||||||
|
type: 'boolean',
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Checkbox',
|
||||||
|
'x-content': '只显示 LOGO',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -38,7 +38,7 @@ export const SiteTitle = () => {
|
|||||||
{!loading && data?.logo?.url && (
|
{!loading && data?.logo?.url && (
|
||||||
<img className={'site-logo'} src={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 className={'site-title'}>{data.title}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -63,8 +63,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.site-info {
|
.site-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
.site-logo {
|
.site-logo {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
}
|
}
|
||||||
|
.site-title {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
@ -40,6 +40,7 @@ import {
|
|||||||
import { DatePicker } from '../../schemas/date-picker';
|
import { DatePicker } from '../../schemas/date-picker';
|
||||||
import { Filter } from '../../schemas/filter';
|
import { Filter } from '../../schemas/filter';
|
||||||
import { Form } from '../../schemas/form';
|
import { Form } from '../../schemas/form';
|
||||||
|
import { ActionLogs } from '../../schemas/action-logs';
|
||||||
import { Grid } from '../../schemas/grid';
|
import { Grid } from '../../schemas/grid';
|
||||||
import { IconPicker } from '../../schemas/icon-picker';
|
import { IconPicker } from '../../schemas/icon-picker';
|
||||||
import { Input } from '../../schemas/input';
|
import { Input } from '../../schemas/input';
|
||||||
@ -192,6 +193,8 @@ export const SchemaField = createSchemaField({
|
|||||||
Tabs,
|
Tabs,
|
||||||
TimePicker,
|
TimePicker,
|
||||||
Upload,
|
Upload,
|
||||||
|
|
||||||
|
ActionLogs,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
55
packages/client/src/schemas/action-logs/index.tsx
Normal file
55
packages/client/src/schemas/action-logs/index.tsx
Normal 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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
@ -264,7 +264,7 @@ Action.Dropdown = observer((props: any) => {
|
|||||||
document.body,
|
document.body,
|
||||||
)}
|
)}
|
||||||
<Dropdown
|
<Dropdown
|
||||||
trigger={['click']}
|
trigger={['hover']}
|
||||||
{...props}
|
{...props}
|
||||||
overlay={
|
overlay={
|
||||||
<Menu>
|
<Menu>
|
||||||
|
@ -397,6 +397,7 @@ function generateCardItemSchema(component) {
|
|||||||
rowKey: 'id',
|
rowKey: 'id',
|
||||||
// dragSort: true,
|
// dragSort: true,
|
||||||
showIndex: true,
|
showIndex: true,
|
||||||
|
defaultSort: ['-id'],
|
||||||
defaultAppends: ['user', 'collection'],
|
defaultAppends: ['user', 'collection'],
|
||||||
refreshRequestOnChange: true,
|
refreshRequestOnChange: true,
|
||||||
pagination: {
|
pagination: {
|
||||||
@ -459,9 +460,9 @@ function generateCardItemSchema(component) {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
'x-component': 'Select',
|
'x-component': 'Select',
|
||||||
enum: [
|
enum: [
|
||||||
{ label: '新增数据', value: 'create' },
|
{ label: '新增', value: 'create', color: 'green' },
|
||||||
{ label: '更新数据', value: 'update' },
|
{ label: '更新', value: 'update', color: 'blue' },
|
||||||
{ label: '删除数据', value: 'destroy' },
|
{ label: '删除', value: 'destroy', color: 'red' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -521,7 +522,7 @@ function generateCardItemSchema(component) {
|
|||||||
properties: {
|
properties: {
|
||||||
created_at: {
|
created_at: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: '创建时间',
|
title: '操作时间',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'DatePicker',
|
'x-component': 'DatePicker',
|
||||||
'x-read-pretty': true,
|
'x-read-pretty': true,
|
||||||
@ -529,6 +530,20 @@ function generateCardItemSchema(component) {
|
|||||||
format: 'YYYY-MM-DD HH:mm:ss',
|
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: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: '操作类型',
|
title: '操作类型',
|
||||||
@ -536,9 +551,21 @@ function generateCardItemSchema(component) {
|
|||||||
'x-component': 'Select',
|
'x-component': 'Select',
|
||||||
'x-read-pretty': true,
|
'x-read-pretty': true,
|
||||||
enum: [
|
enum: [
|
||||||
{ label: '新增数据', value: 'create' },
|
{
|
||||||
{ label: '更新数据', value: 'update' },
|
label: '新增',
|
||||||
{ label: '删除数据', value: 'destroy' },
|
value: 'create',
|
||||||
|
color: 'green',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '更新',
|
||||||
|
value: 'update',
|
||||||
|
color: 'blue',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '删除',
|
||||||
|
value: 'destroy',
|
||||||
|
color: 'red',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
changes: {
|
changes: {
|
||||||
@ -550,18 +577,33 @@ function generateCardItemSchema(component) {
|
|||||||
pagination: false,
|
pagination: false,
|
||||||
// scroll: { x: '100%' },
|
// scroll: { x: '100%' },
|
||||||
},
|
},
|
||||||
|
// 'x-reactions': ['{{ filterActionLogs }}'],
|
||||||
items: {
|
items: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
|
column0: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayTable.Column',
|
||||||
|
'x-component-props': {
|
||||||
|
width: 80,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
index: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'ArrayTable.Index',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
column1: {
|
column1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'ArrayTable.Column',
|
'x-component': 'ArrayTable.Column',
|
||||||
'x-component-props': { title: '字段名称' },
|
'x-component-props': { title: '字段名称' },
|
||||||
properties: {
|
properties: {
|
||||||
'field.uiSchema.title': {
|
field: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
'x-decorator': 'FormilyFormItem',
|
'x-decorator': 'FormilyFormItem',
|
||||||
'x-component': 'Input',
|
'x-component': 'ActionLogs.Field',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -573,7 +615,7 @@ function generateCardItemSchema(component) {
|
|||||||
before: {
|
before: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
'x-decorator': 'FormilyFormItem',
|
'x-decorator': 'FormilyFormItem',
|
||||||
'x-component': 'Values',
|
'x-component': 'ActionLogs.FieldValue',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -585,7 +627,7 @@ function generateCardItemSchema(component) {
|
|||||||
after: {
|
after: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
'x-decorator': 'FormilyFormItem',
|
'x-decorator': 'FormilyFormItem',
|
||||||
'x-component': 'Values',
|
'x-component': 'ActionLogs.FieldValue',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -604,7 +646,7 @@ function generateCardItemSchema(component) {
|
|||||||
},
|
},
|
||||||
column1: {
|
column1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '创建时间',
|
title: '操作时间',
|
||||||
'x-component': 'Table.Column',
|
'x-component': 'Table.Column',
|
||||||
properties: {
|
properties: {
|
||||||
created_at: {
|
created_at: {
|
||||||
@ -618,6 +660,30 @@ function generateCardItemSchema(component) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
column2: {
|
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',
|
type: 'void',
|
||||||
title: '操作类型',
|
title: '操作类型',
|
||||||
'x-component': 'Table.Column',
|
'x-component': 'Table.Column',
|
||||||
@ -627,9 +693,9 @@ function generateCardItemSchema(component) {
|
|||||||
'x-component': 'Select',
|
'x-component': 'Select',
|
||||||
'x-read-pretty': true,
|
'x-read-pretty': true,
|
||||||
enum: [
|
enum: [
|
||||||
{ label: '新增数据', value: 'create' },
|
{ label: '新增', value: 'create', color: 'green' },
|
||||||
{ label: '更新数据', value: 'update' },
|
{ label: '更新', value: 'update', color: 'blue' },
|
||||||
{ label: '删除数据', value: 'destroy' },
|
{ label: '删除', value: 'destroy', color: 'red' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,13 +1,370 @@
|
|||||||
import React from 'react';
|
// import React from 'react';
|
||||||
import { ArrayTable as Table } from '@formily/antd';
|
// import { ArrayTable as Table } from '@formily/antd';
|
||||||
import { useField, Schema } from '@formily/react';
|
// import { useField, Schema } from '@formily/react';
|
||||||
import { Button } from 'antd';
|
// import { Button } from 'antd';
|
||||||
import cls from 'classnames';
|
// import cls from 'classnames';
|
||||||
import { isValid, uid } from '@formily/shared';
|
// import { isValid, uid } from '@formily/shared';
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
// import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { usePrefixCls } from '@formily/antd/lib/__builtins__';
|
// 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) => {
|
const getDefaultValue = (defaultValue: any, schema: Schema) => {
|
||||||
if (isValid(defaultValue)) return defaultValue;
|
if (isValid(defaultValue)) return defaultValue;
|
||||||
@ -26,7 +383,7 @@ const getDefaultValue = (defaultValue: any, schema: Schema) => {
|
|||||||
ArrayTable.Addition = (props: any) => {
|
ArrayTable.Addition = (props: any) => {
|
||||||
const { randomValue } = props;
|
const { randomValue } = props;
|
||||||
const self = useField();
|
const self = useField();
|
||||||
const array = Table.useArray();
|
const array = ArrayBase.useArray();
|
||||||
const prefixCls = usePrefixCls('formily-array-base');
|
const prefixCls = usePrefixCls('formily-array-base');
|
||||||
if (!array) return null;
|
if (!array) return null;
|
||||||
if (array.field?.pattern !== 'editable') return null;
|
if (array.field?.pattern !== 'editable') return null;
|
||||||
|
@ -37,6 +37,7 @@ const schema = {
|
|||||||
loadData: '{{ loadData }}',
|
loadData: '{{ loadData }}',
|
||||||
useDataSource: '{{ useDataSource }}',
|
useDataSource: '{{ useDataSource }}',
|
||||||
labelInValue: true,
|
labelInValue: true,
|
||||||
|
maxLevel: 3,
|
||||||
fieldNames: {
|
fieldNames: {
|
||||||
label: 'name',
|
label: 'name',
|
||||||
value: 'code',
|
value: 'code',
|
||||||
|
@ -32,6 +32,7 @@ export const Cascader = connect(
|
|||||||
fieldNames = defaultFieldNames,
|
fieldNames = defaultFieldNames,
|
||||||
...others
|
...others
|
||||||
} = props;
|
} = props;
|
||||||
|
console.log('Cascader', props)
|
||||||
// 兼容值为 object[] 的情况
|
// 兼容值为 object[] 的情况
|
||||||
const toValue = () => {
|
const toValue = () => {
|
||||||
return toArr(value).map((item) => {
|
return toArr(value).map((item) => {
|
||||||
@ -73,7 +74,7 @@ export const Cascader = connect(
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
// 这里没有用 x-reactions 是因为 readyPretty=true 时不需要
|
// 这里没有用 x-reactions 是因为 readyPretty=true 时不需要
|
||||||
if (useDataSource) {
|
if (typeof useDataSource === 'function') {
|
||||||
useDataSource({
|
useDataSource({
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
field.dataSource = data;
|
field.dataSource = data;
|
||||||
|
@ -4,6 +4,7 @@ import { Checkbox as AntdCheckbox, Tag } from 'antd';
|
|||||||
import { CheckboxProps, CheckboxGroupProps } from 'antd/lib/checkbox';
|
import { CheckboxProps, CheckboxGroupProps } from 'antd/lib/checkbox';
|
||||||
import uniq from 'lodash/uniq';
|
import uniq from 'lodash/uniq';
|
||||||
import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
|
import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
|
||||||
|
import { isValid } from '@formily/shared';
|
||||||
|
|
||||||
type ComposedCheckbox = React.FC<CheckboxProps> & {
|
type ComposedCheckbox = React.FC<CheckboxProps> & {
|
||||||
Group?: React.FC<CheckboxGroupProps>;
|
Group?: React.FC<CheckboxGroupProps>;
|
||||||
@ -25,6 +26,9 @@ export const Checkbox: ComposedCheckbox = connect(
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
mapReadPretty((props) => {
|
mapReadPretty((props) => {
|
||||||
|
if (!isValid(props.value)) {
|
||||||
|
return <div>N/A</div>;
|
||||||
|
}
|
||||||
return props.value ? (
|
return props.value ? (
|
||||||
<CheckOutlined style={{ color: '#52c41a' }} />
|
<CheckOutlined style={{ color: '#52c41a' }} />
|
||||||
) : (
|
) : (
|
||||||
@ -41,6 +45,9 @@ Checkbox.Group = connect(
|
|||||||
dataSource: 'options',
|
dataSource: 'options',
|
||||||
}),
|
}),
|
||||||
mapReadPretty((props) => {
|
mapReadPretty((props) => {
|
||||||
|
if (!isValid(props.value)) {
|
||||||
|
return <div>N/A</div>;
|
||||||
|
}
|
||||||
const { options = [] } = props;
|
const { options = [] } = props;
|
||||||
const field = useField<any>();
|
const field = useField<any>();
|
||||||
const dataSource = field.dataSource || [];
|
const dataSource = field.dataSource || [];
|
||||||
@ -50,7 +57,9 @@ Checkbox.Group = connect(
|
|||||||
{dataSource
|
{dataSource
|
||||||
.filter((option) => value.includes(option.value))
|
.filter((option) => value.includes(option.value))
|
||||||
.map((option, key) => (
|
.map((option, key) => (
|
||||||
<Tag key={key} color={option.color}>{option.label}</Tag>
|
<Tag key={key} color={option.color}>
|
||||||
|
{option.label}
|
||||||
|
</Tag>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,33 +1,36 @@
|
|||||||
import React, { createContext, useContext } from 'react'
|
import React, { createContext, useContext } from 'react';
|
||||||
import { isArr, isValid } from '@formily/shared'
|
import { isArr, isValid } from '@formily/shared';
|
||||||
import { observer, useField } from '@formily/react'
|
import { observer, useField } from '@formily/react';
|
||||||
import { InputProps } from 'antd/lib/input'
|
import { InputProps } from 'antd/lib/input';
|
||||||
import { InputNumberProps } from 'antd/lib/input-number'
|
import { InputNumberProps } from 'antd/lib/input-number';
|
||||||
import { SelectProps } from 'antd/lib/select'
|
import { SelectProps } from 'antd/lib/select';
|
||||||
import { TreeSelectProps } from 'antd/lib/tree-select'
|
import { TreeSelectProps } from 'antd/lib/tree-select';
|
||||||
import { CascaderProps } from 'antd/lib/cascader'
|
import { CascaderProps } from 'antd/lib/cascader';
|
||||||
import {
|
import {
|
||||||
DatePickerProps,
|
DatePickerProps,
|
||||||
RangePickerProps as DateRangePickerProps,
|
RangePickerProps as DateRangePickerProps,
|
||||||
} from 'antd/lib/date-picker'
|
} from 'antd/lib/date-picker';
|
||||||
import { TimePickerProps, TimeRangePickerProps } from 'antd/lib/time-picker'
|
import { TimePickerProps, TimeRangePickerProps } from 'antd/lib/time-picker';
|
||||||
import { Tag, Space, Popover } from 'antd'
|
import { Tag, Space, Popover } from 'antd';
|
||||||
import cls from 'classnames'
|
import cls from 'classnames';
|
||||||
import { formatMomentValue, usePrefixCls } from '@formily/antd/esm/__builtins__'
|
import {
|
||||||
|
formatMomentValue,
|
||||||
|
usePrefixCls,
|
||||||
|
} from '@formily/antd/esm/__builtins__';
|
||||||
import { FullscreenOutlined } from '@ant-design/icons';
|
import { FullscreenOutlined } from '@ant-design/icons';
|
||||||
import moment from 'moment';
|
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 usePlaceholder = (value?: any) => {
|
||||||
const placeholder = useContext(PlaceholderContext) || 'N/A'
|
const placeholder = useContext(PlaceholderContext) || 'N/A';
|
||||||
return isValid(value) && value !== '' ? value : placeholder
|
return isValid(value) && value !== '' ? value : placeholder;
|
||||||
}
|
};
|
||||||
|
|
||||||
const Input: React.FC<InputProps> = (props) => {
|
const Input: React.FC<InputProps> = (props) => {
|
||||||
const prefixCls = usePrefixCls('description-text', props)
|
const prefixCls = usePrefixCls('description-text', props);
|
||||||
return (
|
return (
|
||||||
<Space className={cls(prefixCls, props.className)} style={props.style}>
|
<Space className={cls(prefixCls, props.className)} style={props.style}>
|
||||||
{props.addonBefore}
|
{props.addonBefore}
|
||||||
@ -36,14 +39,16 @@ const Input: React.FC<InputProps> = (props) => {
|
|||||||
{props.suffix}
|
{props.suffix}
|
||||||
{props.addonAfter}
|
{props.addonAfter}
|
||||||
</Space>
|
</Space>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const URL: React.FC<InputProps> = (props) => {
|
const URL: React.FC<InputProps> = (props) => {
|
||||||
const prefixCls = usePrefixCls('description-text', props)
|
const prefixCls = usePrefixCls('description-text', props);
|
||||||
const content = props.value && (
|
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 (
|
return (
|
||||||
<Space className={cls(prefixCls, props.className)} style={props.style}>
|
<Space className={cls(prefixCls, props.className)} style={props.style}>
|
||||||
{props.addonBefore}
|
{props.addonBefore}
|
||||||
@ -52,11 +57,11 @@ const URL: React.FC<InputProps> = (props) => {
|
|||||||
{props.suffix}
|
{props.suffix}
|
||||||
{props.addonAfter}
|
{props.addonAfter}
|
||||||
</Space>
|
</Space>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const InputNumber: React.FC<InputProps & InputNumberProps> = (props) => {
|
const InputNumber: React.FC<InputProps & InputNumberProps> = (props) => {
|
||||||
const prefixCls = usePrefixCls('description-text', props)
|
const prefixCls = usePrefixCls('description-text', props);
|
||||||
const value = usePlaceholder(props.value);
|
const value = usePlaceholder(props.value);
|
||||||
return (
|
return (
|
||||||
<Space className={cls(prefixCls, props.className)} style={props.style}>
|
<Space className={cls(prefixCls, props.className)} style={props.style}>
|
||||||
@ -66,16 +71,17 @@ const InputNumber: React.FC<InputProps & InputNumberProps> = (props) => {
|
|||||||
{props.suffix}
|
{props.suffix}
|
||||||
{props.addonAfter}
|
{props.addonAfter}
|
||||||
</Space>
|
</Space>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const TextArea: React.FC<any> = (props) => {
|
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 ellipsis = props.ellipsis === true ? {} : props.ellipsis;
|
||||||
const content = props.ellipsis ? (
|
const content = props.ellipsis ? (
|
||||||
<div>
|
<div>
|
||||||
<Popover content={usePlaceholder(props.value)}>
|
<Popover content={usePlaceholder(props.value)}>
|
||||||
<div style={{
|
<div
|
||||||
|
style={{
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
@ -84,12 +90,15 @@ const TextArea: React.FC<any> = (props) => {
|
|||||||
verticalAlign: 'middle',
|
verticalAlign: 'middle',
|
||||||
marginRight: 10,
|
marginRight: 10,
|
||||||
...ellipsis,
|
...ellipsis,
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{usePlaceholder(props.text || props.value)}
|
{usePlaceholder(props.text || props.value)}
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
) : usePlaceholder(props.value)
|
) : (
|
||||||
|
usePlaceholder(props.value)
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<Space className={cls(prefixCls, props.className)} style={props.style}>
|
<Space className={cls(prefixCls, props.className)} style={props.style}>
|
||||||
{props.addonBefore}
|
{props.addonBefore}
|
||||||
@ -98,199 +107,202 @@ const TextArea: React.FC<any> = (props) => {
|
|||||||
{props.suffix}
|
{props.suffix}
|
||||||
{props.addonAfter}
|
{props.addonAfter}
|
||||||
</Space>
|
</Space>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const Select: React.FC<SelectProps<any>> = observer((props) => {
|
const Select: React.FC<SelectProps<any>> = observer((props) => {
|
||||||
const field = useField<Formily.Core.Models.Field>()
|
const field = useField<Formily.Core.Models.Field>();
|
||||||
const prefixCls = usePrefixCls('description-text', props)
|
const prefixCls = usePrefixCls('description-text', props);
|
||||||
const dataSource: any[] = field?.dataSource?.length
|
const dataSource: any[] = field?.dataSource?.length
|
||||||
? field.dataSource
|
? field.dataSource
|
||||||
: props?.options?.length
|
: props?.options?.length
|
||||||
? props.options
|
? props.options
|
||||||
: []
|
: [];
|
||||||
const placeholder = usePlaceholder()
|
const placeholder = usePlaceholder();
|
||||||
const getSelected = () => {
|
const getSelected = () => {
|
||||||
const value = props.value
|
const value = props.value;
|
||||||
if (props.mode === 'multiple' || props.mode === 'tags') {
|
if (props.mode === 'multiple' || props.mode === 'tags') {
|
||||||
if (props.labelInValue) {
|
if (props.labelInValue) {
|
||||||
return isArr(value) ? value : []
|
return isArr(value) ? value : [];
|
||||||
} else {
|
} else {
|
||||||
return isArr(value)
|
return isArr(value)
|
||||||
? value.map((val) => ({ label: val, value: val }))
|
? value.map((val) => ({ label: val, value: val }))
|
||||||
: []
|
: [];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (props.labelInValue) {
|
if (props.labelInValue) {
|
||||||
return isValid(value) ? [value] : []
|
return isValid(value) ? [value] : [];
|
||||||
} else {
|
} else {
|
||||||
return isValid(value) ? [{ label: value, value }] : []
|
return isValid(value) ? [{ label: value, value }] : [];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getLabels = () => {
|
const getLabels = () => {
|
||||||
const selected = getSelected()
|
const selected = getSelected();
|
||||||
if (!selected.length) return <Tag>{placeholder}</Tag>
|
if (!selected.length) return <Tag>{placeholder}</Tag>;
|
||||||
return selected.map(({ value, label }, key) => {
|
return selected.map(({ value, label }, key) => {
|
||||||
const text =
|
const text =
|
||||||
dataSource?.find((item) => item.value == value)?.label || label
|
dataSource?.find((item) => item.value == value)?.label || label;
|
||||||
return <Tag key={key}>{text || placeholder}</Tag>
|
return <Tag key={key}>{text || placeholder}</Tag>;
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<div className={cls(prefixCls, props.className)} style={props.style}>
|
<div className={cls(prefixCls, props.className)} style={props.style}>
|
||||||
{getLabels()}
|
{getLabels()}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
const ObjectSelect: React.FC<SelectProps<any>> = observer((props) => {
|
const ObjectSelect: React.FC<SelectProps<any>> = observer((props) => {
|
||||||
const field = useField<Formily.Core.Models.Field>()
|
const field = useField<Formily.Core.Models.Field>();
|
||||||
const prefixCls = usePrefixCls('description-text', props)
|
const prefixCls = usePrefixCls('description-text', props);
|
||||||
const dataSource: any[] = field?.dataSource?.length
|
const dataSource: any[] = field?.dataSource?.length
|
||||||
? field.dataSource
|
? field.dataSource
|
||||||
: props?.options?.length
|
: props?.options?.length
|
||||||
? props.options
|
? props.options
|
||||||
: []
|
: [];
|
||||||
const placeholder = usePlaceholder()
|
const placeholder = usePlaceholder();
|
||||||
const getSelected = () => {
|
const getSelected = () => {
|
||||||
const value = props.value
|
const value = props.value;
|
||||||
if (props.mode === 'multiple' || props.mode === 'tags') {
|
if (props.mode === 'multiple' || props.mode === 'tags') {
|
||||||
if (props.labelInValue) {
|
if (props.labelInValue) {
|
||||||
return isArr(value) ? value : []
|
return isArr(value) ? value : [];
|
||||||
} else {
|
} else {
|
||||||
return isArr(value)
|
return isArr(value)
|
||||||
? value.map((val) => ({ label: val, value: val }))
|
? value.map((val) => ({ label: val, value: val }))
|
||||||
: []
|
: [];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (props.labelInValue) {
|
if (props.labelInValue) {
|
||||||
return isValid(value) ? [value] : []
|
return isValid(value) ? [value] : [];
|
||||||
} else {
|
} else {
|
||||||
return isValid(value) ? [{ label: value, value }] : []
|
return isValid(value) ? [{ label: value, value }] : [];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getLabels = () => {
|
const getLabels = () => {
|
||||||
const selected = getSelected()
|
const selected = getSelected();
|
||||||
if (!selected.length) return <Tag>{placeholder}</Tag>
|
if (!selected.length) return <Tag>{placeholder}</Tag>;
|
||||||
return selected.map(({ value, label }, key) => {
|
return selected.map(({ value, label }, key) => {
|
||||||
const text =
|
const text =
|
||||||
dataSource?.find((item) => item.value == value)?.label || label
|
dataSource?.find((item) => item.value == value)?.label || label;
|
||||||
return <Tag key={key}>{text || placeholder}</Tag>
|
return <Tag key={key}>{text || placeholder}</Tag>;
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<div className={cls(prefixCls, props.className)} style={props.style}>
|
<div className={cls(prefixCls, props.className)} style={props.style}>
|
||||||
{getLabels()}
|
{getLabels()}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
const TreeSelect: React.FC<TreeSelectProps<any>> = observer((props) => {
|
const TreeSelect: React.FC<TreeSelectProps<any>> = observer((props) => {
|
||||||
const field = useField<Formily.Core.Models.Field>()
|
const field = useField<Formily.Core.Models.Field>();
|
||||||
const placeholder = usePlaceholder()
|
const placeholder = usePlaceholder();
|
||||||
const prefixCls = usePrefixCls('description-text', props)
|
const prefixCls = usePrefixCls('description-text', props);
|
||||||
const dataSource = field?.dataSource?.length
|
const dataSource = field?.dataSource?.length
|
||||||
? field.dataSource
|
? field.dataSource
|
||||||
: props?.options?.length
|
: props?.options?.length
|
||||||
? props.options
|
? props.options
|
||||||
: []
|
: [];
|
||||||
const getSelected = () => {
|
const getSelected = () => {
|
||||||
const value = props.value
|
const value = props.value;
|
||||||
if (props.multiple) {
|
if (props.multiple) {
|
||||||
if (props.labelInValue) {
|
if (props.labelInValue) {
|
||||||
return isArr(value) ? value : []
|
return isArr(value) ? value : [];
|
||||||
} else {
|
} else {
|
||||||
return isArr(value)
|
return isArr(value)
|
||||||
? value.map((val) => ({ label: val, value: val }))
|
? value.map((val) => ({ label: val, value: val }))
|
||||||
: []
|
: [];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (props.labelInValue) {
|
if (props.labelInValue) {
|
||||||
return value ? [value] : []
|
return value ? [value] : [];
|
||||||
} else {
|
} else {
|
||||||
return value ? [{ label: value, value }] : []
|
return value ? [{ label: value, value }] : [];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const findLabel = (value: any, dataSource: any[]) => {
|
const findLabel = (value: any, dataSource: any[]) => {
|
||||||
for (let i = 0; i < dataSource?.length; i++) {
|
for (let i = 0; i < dataSource?.length; i++) {
|
||||||
const item = dataSource[i]
|
const item = dataSource[i];
|
||||||
if (item?.value === value) {
|
if (item?.value === value) {
|
||||||
return item?.label
|
return item?.label;
|
||||||
} else {
|
} else {
|
||||||
const childLabel = findLabel(value, item?.children)
|
const childLabel = findLabel(value, item?.children);
|
||||||
if (childLabel) return childLabel
|
if (childLabel) return childLabel;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getLabels = () => {
|
const getLabels = () => {
|
||||||
const selected = getSelected()
|
const selected = getSelected();
|
||||||
if (!selected?.length) return <Tag>{placeholder}</Tag>
|
if (!selected?.length) return <Tag>{placeholder}</Tag>;
|
||||||
return selected.map(({ value, label }, key) => {
|
return selected.map(({ value, label }, key) => {
|
||||||
return (
|
return (
|
||||||
<Tag key={key}>
|
<Tag key={key}>
|
||||||
{findLabel(value, dataSource) || label || placeholder}
|
{findLabel(value, dataSource) || label || placeholder}
|
||||||
</Tag>
|
</Tag>
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<div className={cls(prefixCls, props.className)} style={props.style}>
|
<div className={cls(prefixCls, props.className)} style={props.style}>
|
||||||
{getLabels()}
|
{getLabels()}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
const Cascader: React.FC<CascaderProps> = observer((props) => {
|
const Cascader: React.FC<CascaderProps> = observer((props) => {
|
||||||
const field = useField<Formily.Core.Models.Field>()
|
const field = useField<Formily.Core.Models.Field>();
|
||||||
const placeholder = usePlaceholder()
|
const placeholder = usePlaceholder();
|
||||||
const prefixCls = usePrefixCls('description-text', props)
|
const prefixCls = usePrefixCls('description-text', props);
|
||||||
const dataSource: any[] = field?.dataSource?.length
|
const dataSource: any[] = field?.dataSource?.length
|
||||||
? field.dataSource
|
? field.dataSource
|
||||||
: props?.options?.length
|
: props?.options?.length
|
||||||
? props.options
|
? props.options
|
||||||
: []
|
: [];
|
||||||
const getSelected = () => {
|
const getSelected = () => {
|
||||||
return isArr(props.value) ? props.value : []
|
return isArr(props.value) ? props.value : [];
|
||||||
}
|
};
|
||||||
const findLabel = (value: any, dataSource: any[]) => {
|
const findLabel = (value: any, dataSource: any[]) => {
|
||||||
for (let i = 0; i < dataSource?.length; i++) {
|
for (let i = 0; i < dataSource?.length; i++) {
|
||||||
const item = dataSource[i]
|
const item = dataSource[i];
|
||||||
if (item?.value === value) {
|
if (item?.value === value) {
|
||||||
return item?.label
|
return item?.label;
|
||||||
} else {
|
} else {
|
||||||
const childLabel = findLabel(value, item?.children)
|
const childLabel = findLabel(value, item?.children);
|
||||||
if (childLabel) return childLabel
|
if (childLabel) return childLabel;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
const getLabels = () => {
|
const getLabels = () => {
|
||||||
const selected = getSelected()
|
const selected = getSelected();
|
||||||
if (!selected?.length) {
|
if (!selected?.length) {
|
||||||
return placeholder
|
return placeholder;
|
||||||
}
|
}
|
||||||
return selected
|
return selected
|
||||||
.map((value) => {
|
.map((value) => {
|
||||||
return findLabel(value, dataSource) || placeholder
|
return findLabel(value, dataSource) || placeholder;
|
||||||
})
|
})
|
||||||
.join('/')
|
.join('/');
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<div className={cls(prefixCls, props.className)} style={props.style}>
|
<div className={cls(prefixCls, props.className)} style={props.style}>
|
||||||
{getLabels()}
|
{getLabels()}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
const DatePicker: React.FC<DatePickerProps> = (props: any) => {
|
const DatePicker: React.FC<DatePickerProps> = (props: any) => {
|
||||||
const placeholder = usePlaceholder()
|
if (!props.value) {
|
||||||
const prefixCls = usePrefixCls('description-text', props)
|
return <div>N/A</div>;
|
||||||
|
}
|
||||||
|
const placeholder = usePlaceholder();
|
||||||
|
const prefixCls = usePrefixCls('description-text', props);
|
||||||
const getDefaultFormat = () => {
|
const getDefaultFormat = () => {
|
||||||
const { dateFormat, showTime, timeFormat } = props;
|
const { dateFormat, showTime, timeFormat } = props;
|
||||||
let format = dateFormat;
|
let format = dateFormat;
|
||||||
@ -298,56 +310,60 @@ const DatePicker: React.FC<DatePickerProps> = (props: any) => {
|
|||||||
format += ` ${timeFormat}`;
|
format += ` ${timeFormat}`;
|
||||||
}
|
}
|
||||||
return format || props.format;
|
return format || props.format;
|
||||||
}
|
};
|
||||||
const getLabels = () => {
|
const getLabels = () => {
|
||||||
const d = moment(props.value);
|
const d = moment(props.value);
|
||||||
const labels = formatMomentValue(d.isValid() ? d : null, getDefaultFormat(), placeholder)
|
const labels = formatMomentValue(
|
||||||
return isArr(labels) ? labels.join('~') : labels
|
d.isValid() ? d : null,
|
||||||
}
|
getDefaultFormat(),
|
||||||
return <div className={cls(prefixCls, props.className)}>{getLabels()}</div>
|
placeholder,
|
||||||
}
|
);
|
||||||
|
return isArr(labels) ? labels.join('~') : labels;
|
||||||
|
};
|
||||||
|
return <div className={cls(prefixCls, props.className)}>{getLabels()}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
const DateRangePicker: React.FC<DateRangePickerProps> = (props) => {
|
const DateRangePicker: React.FC<DateRangePickerProps> = (props) => {
|
||||||
const placeholder = usePlaceholder()
|
const placeholder = usePlaceholder();
|
||||||
const prefixCls = usePrefixCls('description-text', props)
|
const prefixCls = usePrefixCls('description-text', props);
|
||||||
const getLabels = () => {
|
const getLabels = () => {
|
||||||
const labels = formatMomentValue(props.value, props.format, placeholder)
|
const labels = formatMomentValue(props.value, props.format, placeholder);
|
||||||
return isArr(labels) ? labels.join('~') : labels
|
return isArr(labels) ? labels.join('~') : labels;
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<div className={cls(prefixCls, props.className)} style={props.style}>
|
<div className={cls(prefixCls, props.className)} style={props.style}>
|
||||||
{getLabels()}
|
{getLabels()}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const TimePicker: React.FC<TimePickerProps> = (props) => {
|
const TimePicker: React.FC<TimePickerProps> = (props) => {
|
||||||
const placeholder = usePlaceholder()
|
const placeholder = usePlaceholder();
|
||||||
const prefixCls = usePrefixCls('description-text', props)
|
const prefixCls = usePrefixCls('description-text', props);
|
||||||
const getLabels = () => {
|
const getLabels = () => {
|
||||||
const labels = formatMomentValue(props.value, props.format, placeholder)
|
const labels = formatMomentValue(props.value, props.format, placeholder);
|
||||||
return isArr(labels) ? labels.join('~') : labels
|
return isArr(labels) ? labels.join('~') : labels;
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<div className={cls(prefixCls, props.className)} style={props.style}>
|
<div className={cls(prefixCls, props.className)} style={props.style}>
|
||||||
{getLabels()}
|
{getLabels()}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const TimeRangePicker: React.FC<TimeRangePickerProps> = (props) => {
|
const TimeRangePicker: React.FC<TimeRangePickerProps> = (props) => {
|
||||||
const placeholder = usePlaceholder()
|
const placeholder = usePlaceholder();
|
||||||
const prefixCls = usePrefixCls('description-text', props)
|
const prefixCls = usePrefixCls('description-text', props);
|
||||||
const getLabels = () => {
|
const getLabels = () => {
|
||||||
const labels = formatMomentValue(props.value, props.format, placeholder)
|
const labels = formatMomentValue(props.value, props.format, placeholder);
|
||||||
return isArr(labels) ? labels.join('~') : labels
|
return isArr(labels) ? labels.join('~') : labels;
|
||||||
}
|
};
|
||||||
return (
|
return (
|
||||||
<div className={cls(prefixCls, props.className)} style={props.style}>
|
<div className={cls(prefixCls, props.className)} style={props.style}>
|
||||||
{getLabels()}
|
{getLabels()}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export const Display = {
|
export const Display = {
|
||||||
Input,
|
Input,
|
||||||
@ -364,6 +380,6 @@ export const Display = {
|
|||||||
Placeholder,
|
Placeholder,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
usePlaceholder,
|
usePlaceholder,
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Display;
|
export default Display;
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
hasIcon,
|
hasIcon,
|
||||||
IconPicker as Icon,
|
IconPicker as Icon,
|
||||||
} from '../../components/icon-picker';
|
} from '../../components/icon-picker';
|
||||||
|
import { isValid } from '@formily/shared';
|
||||||
|
|
||||||
function IconField(props: any) {
|
function IconField(props: any) {
|
||||||
const { value, onChange } = props;
|
const { value, onChange } = props;
|
||||||
@ -72,6 +73,9 @@ export const IconPicker = connect(
|
|||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
mapReadPretty((props) => {
|
mapReadPretty((props) => {
|
||||||
|
if (!isValid(props.value)) {
|
||||||
|
return <div>N/A</div>;
|
||||||
|
}
|
||||||
return <Icon type={props.value} />;
|
return <Icon type={props.value} />;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -34,8 +34,8 @@ export const InputNumber: any = connect(
|
|||||||
},
|
},
|
||||||
mapReadPretty((props: any) => {
|
mapReadPretty((props: any) => {
|
||||||
const { step, value, addonBefore, addonAfter } = props;
|
const { step, value, addonBefore, addonAfter } = props;
|
||||||
if (!isValid(value)) {
|
if (!isValid(props.value)) {
|
||||||
return null;
|
return <div>N/A</div>;
|
||||||
}
|
}
|
||||||
const precision = Math.max(
|
const precision = Math.max(
|
||||||
getNumberPrecision(String(value)),
|
getNumberPrecision(String(value)),
|
||||||
@ -55,8 +55,8 @@ InputNumber.Percent = connect(
|
|||||||
AntdNumber,
|
AntdNumber,
|
||||||
mapReadPretty((props: any) => {
|
mapReadPretty((props: any) => {
|
||||||
const { step, value } = props;
|
const { step, value } = props;
|
||||||
if (!isValid(value)) {
|
if (!isValid(props.value)) {
|
||||||
return null;
|
return <div>N/A</div>;
|
||||||
}
|
}
|
||||||
return toFixed(
|
return toFixed(
|
||||||
String(value),
|
String(value),
|
||||||
|
@ -3,12 +3,14 @@ import { connect, mapReadPretty } from '@formily/react';
|
|||||||
import { Input } from 'antd';
|
import { Input } from 'antd';
|
||||||
import { PasswordProps } from 'antd/lib/input';
|
import { PasswordProps } from 'antd/lib/input';
|
||||||
import { PasswordStrength } from './PasswordStrength';
|
import { PasswordStrength } from './PasswordStrength';
|
||||||
|
import { isValid } from '@formily/shared';
|
||||||
|
|
||||||
export interface IPasswordProps extends PasswordProps {
|
export interface IPasswordProps extends PasswordProps {
|
||||||
checkStrength: boolean;
|
checkStrength: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Password = connect((props: IPasswordProps) => {
|
export const Password = connect(
|
||||||
|
(props: IPasswordProps) => {
|
||||||
const { value, className, checkStrength, ...others } = props;
|
const { value, className, checkStrength, ...others } = props;
|
||||||
const blockStyle: React.CSSProperties = {
|
const blockStyle: React.CSSProperties = {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@ -56,10 +58,13 @@ export const Password = connect((props: IPasswordProps) => {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}, mapReadPretty(() => {
|
},
|
||||||
return (
|
mapReadPretty((props) => {
|
||||||
<div>********</div>
|
if (!isValid(props.value)) {
|
||||||
)
|
return <div>N/A</div>;
|
||||||
}));
|
}
|
||||||
|
return <div>********</div>;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export default Password;
|
export default Password;
|
||||||
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import { connect, mapProps, mapReadPretty, SchemaOptionsContext, useField } from '@formily/react'
|
import { connect, mapProps, mapReadPretty, SchemaOptionsContext, useField } from '@formily/react'
|
||||||
import { Radio as AntdRadio, Tag } from 'antd'
|
import { Radio as AntdRadio, Tag } from 'antd'
|
||||||
import { RadioProps, RadioGroupProps } from 'antd/lib/radio'
|
import { RadioProps, RadioGroupProps } from 'antd/lib/radio'
|
||||||
|
import { isValid } from '@formily/shared';
|
||||||
|
|
||||||
type ComposedRadio = React.FC<RadioProps> & {
|
type ComposedRadio = React.FC<RadioProps> & {
|
||||||
Group?: React.FC<RadioGroupProps>
|
Group?: React.FC<RadioGroupProps>
|
||||||
@ -24,6 +25,9 @@ Radio.Group = connect(
|
|||||||
dataSource: 'options',
|
dataSource: 'options',
|
||||||
}),
|
}),
|
||||||
mapReadPretty((props) => {
|
mapReadPretty((props) => {
|
||||||
|
if (!isValid(props.value)) {
|
||||||
|
return <div>N/A</div>;
|
||||||
|
}
|
||||||
const { options = [], value } = props;
|
const { options = [], value } = props;
|
||||||
const field = useField<any>();
|
const field = useField<any>();
|
||||||
const dataSource = field.dataSource || [];
|
const dataSource = field.dataSource || [];
|
||||||
|
@ -18,7 +18,7 @@ import { useDesignable } from '../';
|
|||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import { isField } from '@formily/core';
|
import { Field, isArrayField, isField } from '@formily/core';
|
||||||
|
|
||||||
export const Select: any = connect(
|
export const Select: any = connect(
|
||||||
(props) => {
|
(props) => {
|
||||||
@ -65,7 +65,15 @@ export const Select: any = connect(
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
mapReadPretty((props) => {
|
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>();
|
const field = useField<any>();
|
||||||
|
if (isArrayField(field) && field?.value?.length === 0) {
|
||||||
|
return <div>N/A</div>;
|
||||||
|
}
|
||||||
const dataSource = field.dataSource || [];
|
const dataSource = field.dataSource || [];
|
||||||
console.log('field.value', field.value, dataSource);
|
console.log('field.value', field.value, dataSource);
|
||||||
const values = toArr(field.value);
|
const values = toArr(field.value);
|
||||||
|
@ -2244,7 +2244,7 @@ Table.useActionLogDetailsResource = ({ onSuccess }) => {
|
|||||||
});
|
});
|
||||||
const service = useRequest(
|
const service = useRequest(
|
||||||
(params?: any) => {
|
(params?: any) => {
|
||||||
return resource.get({ ...params, appends: 'changes' });
|
return resource.get({ ...params, appends: ['changes', 'user', 'collection'] });
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
formatResult: (result) => result?.data,
|
formatResult: (result) => result?.data,
|
||||||
|
Loading…
Reference in New Issue
Block a user