diff --git a/packages/client/src/components/admin-layout/More.tsx b/packages/client/src/components/admin-layout/More.tsx
index 133ad71d5..4367d2062 100644
--- a/packages/client/src/components/admin-layout/More.tsx
+++ b/packages/client/src/components/admin-layout/More.tsx
@@ -80,6 +80,12 @@ const schema: ISchema = {
// accept: 'jpg,png'
},
},
+ showLogoOnly: {
+ type: 'boolean',
+ 'x-decorator': 'FormItem',
+ 'x-component': 'Checkbox',
+ 'x-content': '只显示 LOGO',
+ },
},
},
},
diff --git a/packages/client/src/components/admin-layout/SiteTitle.tsx b/packages/client/src/components/admin-layout/SiteTitle.tsx
index 76bd7a57a..0667d4a4c 100644
--- a/packages/client/src/components/admin-layout/SiteTitle.tsx
+++ b/packages/client/src/components/admin-layout/SiteTitle.tsx
@@ -38,7 +38,7 @@ export const SiteTitle = () => {
{!loading && data?.logo?.url && (
)}
- {!loading && data.title && (
+ {!loading && !data.showLogoOnly && data.title && (
{data.title}
)}
diff --git a/packages/client/src/components/admin-layout/style.less b/packages/client/src/components/admin-layout/style.less
index 8968c303b..6dd5c7b3d 100644
--- a/packages/client/src/components/admin-layout/style.less
+++ b/packages/client/src/components/admin-layout/style.less
@@ -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;
+ }
}
\ No newline at end of file
diff --git a/packages/client/src/components/schema-renderer/index.tsx b/packages/client/src/components/schema-renderer/index.tsx
index 37b887ff9..9dd8c709c 100644
--- a/packages/client/src/components/schema-renderer/index.tsx
+++ b/packages/client/src/components/schema-renderer/index.tsx
@@ -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,
},
});
diff --git a/packages/client/src/schemas/action-logs/index.tsx b/packages/client/src/schemas/action-logs/index.tsx
new file mode 100644
index 000000000..67bf1e867
--- /dev/null
+++ b/packages/client/src/schemas/action-logs/index.tsx
@@ -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 {value?.uiSchema?.title || value?.name}
;
+});
+
+ActionLogs.FieldValue = observer((props: any) => {
+ const field = useField();
+ 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 N/A
;
+ }
+ if (typeof field.value === 'boolean') {
+ return {field.value ? 'true' : 'false'}
;
+ }
+ if (typeof field.value === 'string' || typeof field.value === 'number') {
+ return {field.value}
;
+ }
+ return {JSON.stringify(field.value, null, 2)}
;
+ }
+
+ return (
+
+ );
+});
diff --git a/packages/client/src/schemas/action/index.tsx b/packages/client/src/schemas/action/index.tsx
index f158120b0..59ac7f4f4 100644
--- a/packages/client/src/schemas/action/index.tsx
+++ b/packages/client/src/schemas/action/index.tsx
@@ -264,7 +264,7 @@ Action.Dropdown = observer((props: any) => {
document.body,
)}
diff --git a/packages/client/src/schemas/add-new/index.tsx b/packages/client/src/schemas/add-new/index.tsx
index e26e18a6b..8f8bd596a 100644
--- a/packages/client/src/schemas/add-new/index.tsx
+++ b/packages/client/src/schemas/add-new/index.tsx
@@ -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' },
],
},
},
diff --git a/packages/client/src/schemas/array-table/index.tsx b/packages/client/src/schemas/array-table/index.tsx
index ca3ebc04d..66f6edc82 100644
--- a/packages/client/src/schemas/array-table/index.tsx
+++ b/packages/client/src/schemas/array-table/index.tsx
@@ -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;
+ 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 {
+ pageSize?: number;
+}
+
+type ComposedArrayTable = React.FC> &
+ ArrayBaseMixins & {
+ Column?: React.FC>;
+ };
+
+const SortableRow = SortableElement((props: any) =>
);
+const SortableBody = SortableContainer((props: any) => );
+
+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['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 = (
+
+
+
+ );
+ return children;
+ },
+ });
+ }, []);
+};
+
+const useAddition = () => {
+ const schema = useFieldSchema();
+ return schema.reduceProperties((addition, schema, key) => {
+ if (isAdditionComponent(schema)) {
+ return ;
+ }
+ return addition;
+ }, null);
+};
+
+const StatusSelect: React.FC = observer((props) => {
+ const form = useForm();
+ const field = useField();
+ 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 ? {label} : label,
+ value,
+ };
+ });
+
+ const width = String(options?.length).length * 15;
+
+ return (
+
+ );
+});
+
+const ArrayTablePagination: React.FC = (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 (
+
+ );
+ };
+
+ return (
+
+ {props.paging
+ ? props.children?.(
+ dataSource?.slice(startIndex, endIndex + 1),
+ renderPagination(),
+ )
+ : props.children?.(dataSource, null)}
+
+ );
+};
+
+export const ArrayTable: ComposedArrayTable = observer(
+ (props: TableProps) => {
+ const ref = useRef();
+ const field = useField();
+ 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 (
+
+ {(dataSource, pager) => (
+
+
+ {}}
+ pagination={false}
+ columns={columns}
+ dataSource={dataSource}
+ components={{
+ body: {
+ wrapper: (props: any) => (
+ {
+ return ref.current?.querySelector('tbody');
+ }}
+ onSortStart={({ node }) => {
+ addTdStyles(node);
+ }}
+ onSortEnd={({ oldIndex, newIndex }) => {
+ field.move(oldIndex, newIndex);
+ }}
+ {...props}
+ />
+ ),
+ row: (props: any) => {
+ return (
+
+ );
+ },
+ },
+ }}
+ />
+ {pager}
+ {sources.map((column, key) => {
+ //专门用来承接对Column的状态管理
+ if (!isColumnComponent(column.schema)) return;
+ return React.createElement(RecursionField, {
+ name: column.name,
+ schema: column.schema,
+ onlyRenderSelf: true,
+ key,
+ });
+ })}
+ {addition}
+
+
+ )}
+
+ );
+ },
+);
+
+ArrayTable.displayName = 'ArrayTable';
+
+ArrayTable.Column = () => {
+ return ;
+};
+
+ArrayBase.mixin(ArrayTable);
+
+ArrayTable.Index = (props) => {
+ const index = ArrayBase.useIndex();
+ return {index + 1};
+};
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;
diff --git a/packages/client/src/schemas/cascader/demos/demo2.tsx b/packages/client/src/schemas/cascader/demos/demo2.tsx
index a4ce903b4..2cd1ec8c1 100644
--- a/packages/client/src/schemas/cascader/demos/demo2.tsx
+++ b/packages/client/src/schemas/cascader/demos/demo2.tsx
@@ -37,6 +37,7 @@ const schema = {
loadData: '{{ loadData }}',
useDataSource: '{{ useDataSource }}',
labelInValue: true,
+ maxLevel: 3,
fieldNames: {
label: 'name',
value: 'code',
diff --git a/packages/client/src/schemas/cascader/index.tsx b/packages/client/src/schemas/cascader/index.tsx
index 1e704e3a5..c75fe3d4e 100644
--- a/packages/client/src/schemas/cascader/index.tsx
+++ b/packages/client/src/schemas/cascader/index.tsx
@@ -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;
diff --git a/packages/client/src/schemas/checkbox/index.tsx b/packages/client/src/schemas/checkbox/index.tsx
index b0e36397c..acca85161 100644
--- a/packages/client/src/schemas/checkbox/index.tsx
+++ b/packages/client/src/schemas/checkbox/index.tsx
@@ -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 & {
Group?: React.FC;
@@ -25,6 +26,9 @@ export const Checkbox: ComposedCheckbox = connect(
},
),
mapReadPretty((props) => {
+ if (!isValid(props.value)) {
+ return N/A
;
+ }
return props.value ? (
) : (
@@ -41,6 +45,9 @@ Checkbox.Group = connect(
dataSource: 'options',
}),
mapReadPretty((props) => {
+ if (!isValid(props.value)) {
+ return N/A
;
+ }
const { options = [] } = props;
const field = useField();
const dataSource = field.dataSource || [];
@@ -50,7 +57,9 @@ Checkbox.Group = connect(
{dataSource
.filter((option) => value.includes(option.value))
.map((option, key) => (
- {option.label}
+
+ {option.label}
+
))}
);
diff --git a/packages/client/src/schemas/display/index.tsx b/packages/client/src/schemas/display/index.tsx
index 034ed9879..4ca9d266e 100644
--- a/packages/client/src/schemas/display/index.tsx
+++ b/packages/client/src/schemas/display/index.tsx
@@ -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('N/A')
+const PlaceholderContext = createContext('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 = (props) => {
- const prefixCls = usePrefixCls('description-text', props)
+ const prefixCls = usePrefixCls('description-text', props);
return (
{props.addonBefore}
@@ -36,14 +39,16 @@ const Input: React.FC = (props) => {
{props.suffix}
{props.addonAfter}
- )
-}
+ );
+};
const URL: React.FC = (props) => {
- const prefixCls = usePrefixCls('description-text', props)
+ const prefixCls = usePrefixCls('description-text', props);
const content = props.value && (
- {props.value}
- )
+
+ {props.value}
+
+ );
return (
{props.addonBefore}
@@ -52,11 +57,11 @@ const URL: React.FC = (props) => {
{props.suffix}
{props.addonAfter}
- )
-}
+ );
+};
const InputNumber: React.FC = (props) => {
- const prefixCls = usePrefixCls('description-text', props)
+ const prefixCls = usePrefixCls('description-text', props);
const value = usePlaceholder(props.value);
return (
@@ -66,30 +71,34 @@ const InputNumber: React.FC = (props) => {
{props.suffix}
{props.addonAfter}
- )
-}
+ );
+};
const TextArea: React.FC = (props) => {
- const prefixCls = usePrefixCls('description-text', props)
+ const prefixCls = usePrefixCls('description-text', props);
const ellipsis = props.ellipsis === true ? {} : props.ellipsis;
const content = props.ellipsis ? (
-
- {usePlaceholder(props.text||props.value)}
+
+ {usePlaceholder(props.text || props.value)}
- ) : usePlaceholder(props.value)
+ ) : (
+ usePlaceholder(props.value)
+ );
return (
{props.addonBefore}
@@ -98,199 +107,202 @@ const TextArea: React.FC = (props) => {
{props.suffix}
{props.addonAfter}
- )
-}
+ );
+};
const Select: React.FC> = observer((props) => {
- const field = useField()
- const prefixCls = usePrefixCls('description-text', props)
+ const field = useField();
+ 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 {placeholder}
+ const selected = getSelected();
+ if (!selected.length) return {placeholder};
return selected.map(({ value, label }, key) => {
const text =
- dataSource?.find((item) => item.value == value)?.label || label
- return {text || placeholder}
- })
- }
+ dataSource?.find((item) => item.value == value)?.label || label;
+ return {text || placeholder};
+ });
+ };
return (
{getLabels()}
- )
-})
+ );
+});
const ObjectSelect: React.FC> = observer((props) => {
- const field = useField()
- const prefixCls = usePrefixCls('description-text', props)
+ const field = useField();
+ 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 {placeholder}
+ const selected = getSelected();
+ if (!selected.length) return {placeholder};
return selected.map(({ value, label }, key) => {
const text =
- dataSource?.find((item) => item.value == value)?.label || label
- return {text || placeholder}
- })
- }
+ dataSource?.find((item) => item.value == value)?.label || label;
+ return {text || placeholder};
+ });
+ };
return (
{getLabels()}
- )
-})
+ );
+});
const TreeSelect: React.FC> = observer((props) => {
- const field = useField()
- const placeholder = usePlaceholder()
- const prefixCls = usePrefixCls('description-text', props)
+ const field = useField();
+ 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 {placeholder}
+ const selected = getSelected();
+ if (!selected?.length) return {placeholder};
return selected.map(({ value, label }, key) => {
return (
{findLabel(value, dataSource) || label || placeholder}
- )
- })
- }
+ );
+ });
+ };
return (
{getLabels()}
- )
-})
+ );
+});
const Cascader: React.FC = observer((props) => {
- const field = useField()
- const placeholder = usePlaceholder()
- const prefixCls = usePrefixCls('description-text', props)
+ const field = useField();
+ 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 (
{getLabels()}
- )
-})
+ );
+});
const DatePicker: React.FC = (props: any) => {
- const placeholder = usePlaceholder()
- const prefixCls = usePrefixCls('description-text', props)
+ if (!props.value) {
+ return N/A
;
+ }
+ 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 = (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 {getLabels()}
-}
+ const labels = formatMomentValue(
+ d.isValid() ? d : null,
+ getDefaultFormat(),
+ placeholder,
+ );
+ return isArr(labels) ? labels.join('~') : labels;
+ };
+ return {getLabels()}
;
+};
const DateRangePicker: React.FC = (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 (
{getLabels()}
- )
-}
+ );
+};
const TimePicker: React.FC = (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 (
{getLabels()}
- )
-}
+ );
+};
const TimeRangePicker: React.FC = (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 (
{getLabels()}
- )
-}
+ );
+};
export const Display = {
Input,
@@ -364,6 +380,6 @@ export const Display = {
Placeholder,
InputNumber,
usePlaceholder,
-}
+};
export default Display;
diff --git a/packages/client/src/schemas/icon-picker/index.tsx b/packages/client/src/schemas/icon-picker/index.tsx
index e16a58cb3..a0bf73545 100644
--- a/packages/client/src/schemas/icon-picker/index.tsx
+++ b/packages/client/src/schemas/icon-picker/index.tsx
@@ -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 N/A
;
+ }
return ;
}),
);
diff --git a/packages/client/src/schemas/input-number/index.tsx b/packages/client/src/schemas/input-number/index.tsx
index e5e8998d8..22317f688 100644
--- a/packages/client/src/schemas/input-number/index.tsx
+++ b/packages/client/src/schemas/input-number/index.tsx
@@ -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 N/A
;
}
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 N/A
;
}
return toFixed(
String(value),
diff --git a/packages/client/src/schemas/password/index.tsx b/packages/client/src/schemas/password/index.tsx
index 86ad2cb1f..89245bdb8 100644
--- a/packages/client/src/schemas/password/index.tsx
+++ b/packages/client/src/schemas/password/index.tsx
@@ -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 (
-
-
- {checkStrength && (
-
- {(score) => {
- return (
-
-
-
-
-
+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 (
+
+
+ {checkStrength && (
+
+ {(score) => {
+ return (
-
- );
- }}
-
- )}
-
- );
-}, mapReadPretty(() => {
- return (
- ********
- )
-}));
+ >
+
+
+
+
+
+
+ );
+ }}
+
+ )}
+
+ );
+ },
+ mapReadPretty((props) => {
+ if (!isValid(props.value)) {
+ return N/A
;
+ }
+ return ********
;
+ }),
+);
export default Password;
diff --git a/packages/client/src/schemas/radio/index.tsx b/packages/client/src/schemas/radio/index.tsx
index 44c1a972c..b0998c6d8 100644
--- a/packages/client/src/schemas/radio/index.tsx
+++ b/packages/client/src/schemas/radio/index.tsx
@@ -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 & {
Group?: React.FC
@@ -24,6 +25,9 @@ Radio.Group = connect(
dataSource: 'options',
}),
mapReadPretty((props) => {
+ if (!isValid(props.value)) {
+ return N/A
;
+ }
const { options = [], value } = props;
const field = useField();
const dataSource = field.dataSource || [];
diff --git a/packages/client/src/schemas/select/index.tsx b/packages/client/src/schemas/select/index.tsx
index f9bd2d582..3b9298ff9 100644
--- a/packages/client/src/schemas/select/index.tsx
+++ b/packages/client/src/schemas/select/index.tsx
@@ -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 N/A
;
+ if (!isValid(props.value)) {
+ return N/A
;
+ }
const field = useField();
+ if (isArrayField(field) && field?.value?.length === 0) {
+ return N/A
;
+ }
const dataSource = field.dataSource || [];
console.log('field.value', field.value, dataSource);
const values = toArr(field.value);
diff --git a/packages/client/src/schemas/table/index.tsx b/packages/client/src/schemas/table/index.tsx
index 9b38873ec..ff4442a6f 100644
--- a/packages/client/src/schemas/table/index.tsx
+++ b/packages/client/src/schemas/table/index.tsx
@@ -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,