feat: add uuid & nanoid & unitTimestamp interface (#3684)
* refactor: select & radio compoent supports multiple type of value * fix: ridio test * feat: uuid & nanoid & snowflake interface * refactor: delete snowflake * feat: nanoid field type (#3685) * refactor: add child in inheritance of tree collection (#3676) * refactor: add child in inheritance of tree collection * refactor: add child in inheritance of tree collection * style: style improve * feat: nanoid field * chore: nanoid field type map --------- Co-authored-by: katherinehhh <shunai.tang@hand-china.com> * chore: nanoid options * refactor: unixTimestamp * fix: test * refactor: unixTimestamp * refactor: unixTimestamp * refactor: locale imrove * refactor: local improve * refactor: nanoIDInput * refactor: nanoIDInput * refactor: nanoIDInput * refactor: unixTimestamp * refactor: nanoIDInput * fix: test --------- Co-authored-by: ChengLei Shao <chareice@live.com>
This commit is contained in:
		
							parent
							
								
									4015cf1c0d
								
							
						
					
					
						commit
						c7cfeec6a1
					
				@ -40,6 +40,9 @@ import {
 | 
				
			|||||||
  UpdatedByFieldInterface,
 | 
					  UpdatedByFieldInterface,
 | 
				
			||||||
  UrlFieldInterface,
 | 
					  UrlFieldInterface,
 | 
				
			||||||
  SortFieldInterface,
 | 
					  SortFieldInterface,
 | 
				
			||||||
 | 
					  UUIDFieldInterface,
 | 
				
			||||||
 | 
					  NanoidFieldInterface,
 | 
				
			||||||
 | 
					  UnixTimestampFieldInterface,
 | 
				
			||||||
} from './interfaces';
 | 
					} from './interfaces';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  GeneralCollectionTemplate,
 | 
					  GeneralCollectionTemplate,
 | 
				
			||||||
@ -155,6 +158,9 @@ export class CollectionPlugin extends Plugin {
 | 
				
			|||||||
      UpdatedByFieldInterface,
 | 
					      UpdatedByFieldInterface,
 | 
				
			||||||
      UrlFieldInterface,
 | 
					      UrlFieldInterface,
 | 
				
			||||||
      SortFieldInterface,
 | 
					      SortFieldInterface,
 | 
				
			||||||
 | 
					      UUIDFieldInterface,
 | 
				
			||||||
 | 
					      NanoidFieldInterface,
 | 
				
			||||||
 | 
					      UnixTimestampFieldInterface,
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -34,3 +34,6 @@ export * from './updatedAt';
 | 
				
			|||||||
export * from './updatedBy';
 | 
					export * from './updatedBy';
 | 
				
			||||||
export * from './url';
 | 
					export * from './url';
 | 
				
			||||||
export * from './sort';
 | 
					export * from './sort';
 | 
				
			||||||
 | 
					export * from './uuid';
 | 
				
			||||||
 | 
					export * from './nanoid';
 | 
				
			||||||
 | 
					export * from './unixTimestamp';
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface';
 | 
				
			||||||
 | 
					import { operators } from './properties';
 | 
				
			||||||
 | 
					export class NanoidFieldInterface extends CollectionFieldInterface {
 | 
				
			||||||
 | 
					  name = 'nanoid';
 | 
				
			||||||
 | 
					  type = 'object';
 | 
				
			||||||
 | 
					  group = 'advanced';
 | 
				
			||||||
 | 
					  order = 0;
 | 
				
			||||||
 | 
					  title = '{{t("Nano ID")}}';
 | 
				
			||||||
 | 
					  hidden = false;
 | 
				
			||||||
 | 
					  sortable = true;
 | 
				
			||||||
 | 
					  default = {
 | 
				
			||||||
 | 
					    type: 'nanoid',
 | 
				
			||||||
 | 
					    uiSchema: {
 | 
				
			||||||
 | 
					      type: 'string',
 | 
				
			||||||
 | 
					      'x-component': 'NanoIDInput',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  availableTypes = ['string', 'uid'];
 | 
				
			||||||
 | 
					  properties = {
 | 
				
			||||||
 | 
					    'uiSchema.title': {
 | 
				
			||||||
 | 
					      type: 'string',
 | 
				
			||||||
 | 
					      title: '{{t("Field display name")}}',
 | 
				
			||||||
 | 
					      required: true,
 | 
				
			||||||
 | 
					      'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					      'x-component': 'Input',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    name: {
 | 
				
			||||||
 | 
					      type: 'string',
 | 
				
			||||||
 | 
					      title: '{{t("Field name")}}',
 | 
				
			||||||
 | 
					      required: true,
 | 
				
			||||||
 | 
					      'x-disabled': true,
 | 
				
			||||||
 | 
					      'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					      'x-component': 'Input',
 | 
				
			||||||
 | 
					      description:
 | 
				
			||||||
 | 
					        "{{t('Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.')}}",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    customAlphabet: {
 | 
				
			||||||
 | 
					      type: 'string',
 | 
				
			||||||
 | 
					      title: '{{t("Alphabet")}}',
 | 
				
			||||||
 | 
					      default: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
 | 
				
			||||||
 | 
					      'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					      'x-component': 'Input',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    size: {
 | 
				
			||||||
 | 
					      type: 'number',
 | 
				
			||||||
 | 
					      title: '{{t("Length")}}',
 | 
				
			||||||
 | 
					      default: 21,
 | 
				
			||||||
 | 
					      'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					      'x-component': 'InputNumber',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  filterable = {
 | 
				
			||||||
 | 
					    operators: operators.string,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  titleUsable = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface';
 | 
				
			||||||
 | 
					import { dateTimeProps, defaultProps, operators } from './properties';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class UnixTimestampFieldInterface extends CollectionFieldInterface {
 | 
				
			||||||
 | 
					  name = 'unixTimestamp';
 | 
				
			||||||
 | 
					  type = 'object';
 | 
				
			||||||
 | 
					  group = 'datetime';
 | 
				
			||||||
 | 
					  order = 1;
 | 
				
			||||||
 | 
					  title = '{{t("Unix Timestamp")}}';
 | 
				
			||||||
 | 
					  sortable = true;
 | 
				
			||||||
 | 
					  default = {
 | 
				
			||||||
 | 
					    type: 'bigInt',
 | 
				
			||||||
 | 
					    uiSchema: {
 | 
				
			||||||
 | 
					      type: 'number',
 | 
				
			||||||
 | 
					      'x-component': 'UnixTimestamp',
 | 
				
			||||||
 | 
					      'x-component-props': {
 | 
				
			||||||
 | 
					        accuracy: 'millisecond',
 | 
				
			||||||
 | 
					        showTime: true,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  availableTypes = ['integet', 'bigInt'];
 | 
				
			||||||
 | 
					  hasDefaultValue = true;
 | 
				
			||||||
 | 
					  properties = {
 | 
				
			||||||
 | 
					    ...defaultProps,
 | 
				
			||||||
 | 
					    'uiSchema.x-component-props.accuracy': {
 | 
				
			||||||
 | 
					      type: 'string',
 | 
				
			||||||
 | 
					      title: '{{t("Accuracy")}}',
 | 
				
			||||||
 | 
					      'x-component': 'Radio.Group',
 | 
				
			||||||
 | 
					      'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					      default: 'millisecond',
 | 
				
			||||||
 | 
					      enum: [
 | 
				
			||||||
 | 
					        { value: 'millisecond', label: '{{t("Millisecond")}}' },
 | 
				
			||||||
 | 
					        { value: 'second', label: '{{t("Second")}}' },
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  filterable = {
 | 
				
			||||||
 | 
					    operators: operators.number,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  titleUsable = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface';
 | 
				
			||||||
 | 
					import { operators } from './properties';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class UUIDFieldInterface extends CollectionFieldInterface {
 | 
				
			||||||
 | 
					  name = 'uuid';
 | 
				
			||||||
 | 
					  type = 'object';
 | 
				
			||||||
 | 
					  group = 'advanced';
 | 
				
			||||||
 | 
					  order = 0;
 | 
				
			||||||
 | 
					  title = '{{t("UUID")}}';
 | 
				
			||||||
 | 
					  hidden = false;
 | 
				
			||||||
 | 
					  sortable = true;
 | 
				
			||||||
 | 
					  default = {
 | 
				
			||||||
 | 
					    type: 'uuid',
 | 
				
			||||||
 | 
					    uiSchema: {
 | 
				
			||||||
 | 
					      type: 'string',
 | 
				
			||||||
 | 
					      'x-component': 'Input',
 | 
				
			||||||
 | 
					      'x-validator': 'uuid',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  availableTypes = ['string', 'uid'];
 | 
				
			||||||
 | 
					  properties = {
 | 
				
			||||||
 | 
					    'uiSchema.title': {
 | 
				
			||||||
 | 
					      type: 'string',
 | 
				
			||||||
 | 
					      title: '{{t("Field display name")}}',
 | 
				
			||||||
 | 
					      required: true,
 | 
				
			||||||
 | 
					      'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					      'x-component': 'Input',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    name: {
 | 
				
			||||||
 | 
					      type: 'string',
 | 
				
			||||||
 | 
					      title: '{{t("Field name")}}',
 | 
				
			||||||
 | 
					      required: true,
 | 
				
			||||||
 | 
					      'x-disabled': true,
 | 
				
			||||||
 | 
					      'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					      'x-component': 'Input',
 | 
				
			||||||
 | 
					      description:
 | 
				
			||||||
 | 
					        "{{t('Randomly generated and can be modified. Support letters, numbers and underscores, must start with an letter.')}}",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  filterable = {
 | 
				
			||||||
 | 
					    operators: operators.string,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  titleUsable = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -891,5 +891,12 @@
 | 
				
			|||||||
  "Owners": "负责人",
 | 
					  "Owners": "负责人",
 | 
				
			||||||
  "Plugin settings": "插件设置",
 | 
					  "Plugin settings": "插件设置",
 | 
				
			||||||
  "Menu": "菜单",
 | 
					  "Menu": "菜单",
 | 
				
			||||||
  "Drag and drop sorting field": "拖拽排序字段"
 | 
					  "Drag and drop sorting field": "拖拽排序字段",
 | 
				
			||||||
 | 
					  "Alphabet": "字符",
 | 
				
			||||||
 | 
					  "Accuracy": "精确度",
 | 
				
			||||||
 | 
					  "Millisecond": "毫秒",
 | 
				
			||||||
 | 
					  "Second": "秒",
 | 
				
			||||||
 | 
					  "Unix Timestamp": "Unix 时间戳",
 | 
				
			||||||
 | 
					  "Field value do not meet the requirements": "字符不符合要求",
 | 
				
			||||||
 | 
					  "Field value size is": "字符长度要求"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					import { useFieldSchema } from '@formily/react';
 | 
				
			||||||
 | 
					import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
 | 
				
			||||||
 | 
					import { SchemaSettingsDateFormat } from '../../../../schema-settings/SchemaSettingsDateFormat';
 | 
				
			||||||
 | 
					import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const unixTimestampComponentFieldSettings = new SchemaSettings({
 | 
				
			||||||
 | 
					  name: 'fieldSettings:component:UnixTimestamp',
 | 
				
			||||||
 | 
					  items: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: 'dateDisplayFormat',
 | 
				
			||||||
 | 
					      Component: SchemaSettingsDateFormat as any,
 | 
				
			||||||
 | 
					      useComponentProps() {
 | 
				
			||||||
 | 
					        const schema = useFieldSchema();
 | 
				
			||||||
 | 
					        const { fieldSchema: tableColumnSchema } = useColumnSchema();
 | 
				
			||||||
 | 
					        const fieldSchema = tableColumnSchema || schema;
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					          fieldSchema,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -38,6 +38,7 @@ const InternalRangePicker = connect(
 | 
				
			|||||||
export const DatePicker = (props) => {
 | 
					export const DatePicker = (props) => {
 | 
				
			||||||
  const { utc = true } = useDatePickerContext();
 | 
					  const { utc = true } = useDatePickerContext();
 | 
				
			||||||
  const value = Array.isArray(props.value) ? props.value[0] : props.value;
 | 
					  const value = Array.isArray(props.value) ? props.value[0] : props.value;
 | 
				
			||||||
 | 
					  console.log(value);
 | 
				
			||||||
  props = { utc, ...props };
 | 
					  props = { utc, ...props };
 | 
				
			||||||
  return <InternalDatePicker {...props} value={value} />;
 | 
					  return <InternalDatePicker {...props} value={value} />;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -50,5 +50,7 @@ export * from './time-picker';
 | 
				
			|||||||
export * from './tree-select';
 | 
					export * from './tree-select';
 | 
				
			||||||
export * from './upload';
 | 
					export * from './upload';
 | 
				
			||||||
export * from './variable';
 | 
					export * from './variable';
 | 
				
			||||||
 | 
					export * from './unixTimestamp';
 | 
				
			||||||
 | 
					export * from './nanoIDInput';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import './index.less';
 | 
					import './index.less';
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					import { customAlphabet as Alphabet } from 'nanoid';
 | 
				
			||||||
 | 
					import React, { useEffect } from 'react';
 | 
				
			||||||
 | 
					import { LoadingOutlined } from '@ant-design/icons';
 | 
				
			||||||
 | 
					import { connect, mapProps, mapReadPretty, useForm } from '@formily/react';
 | 
				
			||||||
 | 
					import { Input as AntdInput } from 'antd';
 | 
				
			||||||
 | 
					import { ReadPretty } from '../input';
 | 
				
			||||||
 | 
					import { useCollectionField } from '../../../data-source/collection-field/CollectionFieldProvider';
 | 
				
			||||||
 | 
					import { useTranslation } from 'react-i18next';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const NanoIDInput = Object.assign(
 | 
				
			||||||
 | 
					  connect(
 | 
				
			||||||
 | 
					    AntdInput,
 | 
				
			||||||
 | 
					    mapProps((props: any, field: any) => {
 | 
				
			||||||
 | 
					      const { size, customAlphabet } = useCollectionField();
 | 
				
			||||||
 | 
					      const { t } = useTranslation();
 | 
				
			||||||
 | 
					      const form = useForm();
 | 
				
			||||||
 | 
					      function isValidNanoid(value) {
 | 
				
			||||||
 | 
					        if (value.length !== size) {
 | 
				
			||||||
 | 
					          return t('Field value size is') + ` ${size}`;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (let i = 0; i < value.length; i++) {
 | 
				
			||||||
 | 
					          if (customAlphabet.indexOf(value[i]) === -1) {
 | 
				
			||||||
 | 
					            return t(`Field value do not meet the requirements`);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      useEffect(() => {
 | 
				
			||||||
 | 
					        if (!field.initialValue) {
 | 
				
			||||||
 | 
					          field.setInitialValue(Alphabet(customAlphabet, size)());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        form.setFieldState(field.props.name, (state) => {
 | 
				
			||||||
 | 
					          state.validator = isValidNanoid;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }, []);
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        ...props,
 | 
				
			||||||
 | 
					        suffix: <span>{field?.['loading'] || field?.['validating'] ? <LoadingOutlined /> : props.suffix}</span>,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    mapReadPretty(ReadPretty.Input),
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    ReadPretty: ReadPretty.Input,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					export * from './NanoIDInput';
 | 
				
			||||||
@ -21,9 +21,17 @@ Radio.__ANT_RADIO = true;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Radio.Group = connect(
 | 
					Radio.Group = connect(
 | 
				
			||||||
  AntdRadio.Group,
 | 
					  AntdRadio.Group,
 | 
				
			||||||
  mapProps({
 | 
					  mapProps(
 | 
				
			||||||
    dataSource: 'options',
 | 
					    {
 | 
				
			||||||
  }),
 | 
					      dataSource: 'options',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    (props) => {
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        ...props,
 | 
				
			||||||
 | 
					        value: props.value && typeof props.value !== 'boolean' ? props.value.toString() : props.value,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
  mapReadPretty((props) => {
 | 
					  mapReadPretty((props) => {
 | 
				
			||||||
    if (!isValid(props.value)) {
 | 
					    if (!isValid(props.value)) {
 | 
				
			||||||
      return <div></div>;
 | 
					      return <div></div>;
 | 
				
			||||||
 | 
				
			|||||||
@ -9,11 +9,11 @@ import React from 'react';
 | 
				
			|||||||
const options = [
 | 
					const options = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    label: '男',
 | 
					    label: '男',
 | 
				
			||||||
    value: 1,
 | 
					    value: '1',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    label: '女',
 | 
					    label: '女',
 | 
				
			||||||
    value: 2,
 | 
					    value: '2',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,12 +8,12 @@ import React from 'react';
 | 
				
			|||||||
const options = [
 | 
					const options = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    label: '男',
 | 
					    label: '男',
 | 
				
			||||||
    value: 1,
 | 
					    value: '1',
 | 
				
			||||||
    color: 'blue',
 | 
					    color: 'blue',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    label: '女',
 | 
					    label: '女',
 | 
				
			||||||
    value: 2,
 | 
					    value: '2',
 | 
				
			||||||
    color: 'red',
 | 
					    color: 'red',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
				
			|||||||
@ -122,7 +122,7 @@ const InternalSelect = connect(
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        return undefined;
 | 
					        return undefined;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return v;
 | 
					      return v ? v.toString() : v;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <AntdSelect
 | 
					      <AntdSelect
 | 
				
			||||||
 | 
				
			|||||||
@ -44,7 +44,10 @@ export function getCurrentOptions(values: string | string[], dataSource: any[],
 | 
				
			|||||||
    if (!options) return [];
 | 
					    if (!options) return [];
 | 
				
			||||||
    const current: Option[] = [];
 | 
					    const current: Option[] = [];
 | 
				
			||||||
    for (const value of arrValues) {
 | 
					    for (const value of arrValues) {
 | 
				
			||||||
      const option = options.find((v) => v[fieldNames.value] === value) || { value, label: value };
 | 
					      const option = options.find((v) => v[fieldNames.value] == value) || {
 | 
				
			||||||
 | 
					        value,
 | 
				
			||||||
 | 
					        label: value ? value.toString() : value,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
      current.push(option);
 | 
					      current.push(option);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return current;
 | 
					    return current;
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					import { connect, mapReadPretty } from '@formily/react';
 | 
				
			||||||
 | 
					import React, { useMemo } from 'react';
 | 
				
			||||||
 | 
					import { DatePicker } from '../date-picker';
 | 
				
			||||||
 | 
					import dayjs from 'dayjs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const toValue = (value: any, accuracy) => {
 | 
				
			||||||
 | 
					  if (value) {
 | 
				
			||||||
 | 
					    return timestampToDate(value, accuracy);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return null;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function timestampToDate(timestamp, accuracy = 'millisecond') {
 | 
				
			||||||
 | 
					  if (accuracy === 'second') {
 | 
				
			||||||
 | 
					    timestamp *= 1000; // 如果精确度是秒级,则将时间戳乘以1000转换为毫秒级
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return dayjs(timestamp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getTimestamp(date, accuracy = 'millisecond') {
 | 
				
			||||||
 | 
					  if (accuracy === 'second') {
 | 
				
			||||||
 | 
					    return dayjs(date).unix();
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    return dayjs(date).valueOf(); // 默认返回毫秒级时间戳
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const UnixTimestamp = connect(
 | 
				
			||||||
 | 
					  (props) => {
 | 
				
			||||||
 | 
					    const { value, onChange, accuracy } = props;
 | 
				
			||||||
 | 
					    const v = useMemo(() => toValue(value, accuracy), [value]);
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <DatePicker
 | 
				
			||||||
 | 
					        {...props}
 | 
				
			||||||
 | 
					        value={v}
 | 
				
			||||||
 | 
					        onChange={(v: any) => {
 | 
				
			||||||
 | 
					          if (onChange) {
 | 
				
			||||||
 | 
					            onChange(getTimestamp(v, accuracy));
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  mapReadPretty((props) => {
 | 
				
			||||||
 | 
					    const { value, accuracy } = props;
 | 
				
			||||||
 | 
					    const v = useMemo(() => toValue(value, accuracy), [value]);
 | 
				
			||||||
 | 
					    return <DatePicker.ReadPretty {...props} value={v} />;
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					export * from './UnixTimestamp';
 | 
				
			||||||
@ -42,6 +42,7 @@ import { subformPopoverComponentFieldSettings } from '../modules/fields/componen
 | 
				
			|||||||
import { selectComponentFieldSettings } from '../modules/fields/component/Select/selectComponentFieldSettings';
 | 
					import { selectComponentFieldSettings } from '../modules/fields/component/Select/selectComponentFieldSettings';
 | 
				
			||||||
import { subTablePopoverComponentFieldSettings } from '../modules/fields/component/SubTable/subTablePopoverComponentFieldSettings';
 | 
					import { subTablePopoverComponentFieldSettings } from '../modules/fields/component/SubTable/subTablePopoverComponentFieldSettings';
 | 
				
			||||||
import { tagComponentFieldSettings } from '../modules/fields/component/Tag/tagComponentFieldSettings';
 | 
					import { tagComponentFieldSettings } from '../modules/fields/component/Tag/tagComponentFieldSettings';
 | 
				
			||||||
 | 
					import { unixTimestampComponentFieldSettings } from '../modules/fields/component/UnixTimestamp/unixTimestampComponentFieldSettings';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class SchemaSettingsPlugin extends Plugin {
 | 
					export class SchemaSettingsPlugin extends Plugin {
 | 
				
			||||||
  async load() {
 | 
					  async load() {
 | 
				
			||||||
@ -90,6 +91,8 @@ export class SchemaSettingsPlugin extends Plugin {
 | 
				
			|||||||
    this.schemaSettingsManager.add(subformPopoverComponentFieldSettings);
 | 
					    this.schemaSettingsManager.add(subformPopoverComponentFieldSettings);
 | 
				
			||||||
    this.schemaSettingsManager.add(subTablePopoverComponentFieldSettings);
 | 
					    this.schemaSettingsManager.add(subTablePopoverComponentFieldSettings);
 | 
				
			||||||
    this.schemaSettingsManager.add(datePickerComponentFieldSettings);
 | 
					    this.schemaSettingsManager.add(datePickerComponentFieldSettings);
 | 
				
			||||||
 | 
					    this.schemaSettingsManager.add(unixTimestampComponentFieldSettings);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.schemaSettingsManager.add(fileManagerComponentFieldSettings);
 | 
					    this.schemaSettingsManager.add(fileManagerComponentFieldSettings);
 | 
				
			||||||
    this.schemaSettingsManager.add(tagComponentFieldSettings);
 | 
					    this.schemaSettingsManager.add(tagComponentFieldSettings);
 | 
				
			||||||
    this.schemaSettingsManager.add(cascadeSelectComponentFieldSettings);
 | 
					    this.schemaSettingsManager.add(cascadeSelectComponentFieldSettings);
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					import { mockDatabase } from '../';
 | 
				
			||||||
 | 
					import { Database } from '../../database';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('nanoid field', () => {
 | 
				
			||||||
 | 
					  let db: Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeEach(async () => {
 | 
				
			||||||
 | 
					    db = mockDatabase();
 | 
				
			||||||
 | 
					    await db.clean({ drop: true });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  afterEach(async () => {
 | 
				
			||||||
 | 
					    await db.close();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should create nanoid field type', async () => {
 | 
				
			||||||
 | 
					    const Test = db.collection({
 | 
				
			||||||
 | 
					      name: 'tests',
 | 
				
			||||||
 | 
					      autoGenId: false,
 | 
				
			||||||
 | 
					      fields: [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          type: 'nanoid',
 | 
				
			||||||
 | 
					          name: 'id',
 | 
				
			||||||
 | 
					          primaryKey: true,
 | 
				
			||||||
 | 
					          size: 21,
 | 
				
			||||||
 | 
					          customAlphabet: '1234567890abcdef',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          type: 'nanoid',
 | 
				
			||||||
 | 
					          name: 'id2',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    await Test.sync();
 | 
				
			||||||
 | 
					    const test = await Test.model.create();
 | 
				
			||||||
 | 
					    expect(test.id).toHaveLength(21);
 | 
				
			||||||
 | 
					    expect(test.id2).toHaveLength(12);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -25,6 +25,7 @@ import { TimeFieldOptions } from './time-field';
 | 
				
			|||||||
import { UidFieldOptions } from './uid-field';
 | 
					import { UidFieldOptions } from './uid-field';
 | 
				
			||||||
import { UUIDFieldOptions } from './uuid-field';
 | 
					import { UUIDFieldOptions } from './uuid-field';
 | 
				
			||||||
import { VirtualFieldOptions } from './virtual-field';
 | 
					import { VirtualFieldOptions } from './virtual-field';
 | 
				
			||||||
 | 
					import { NanoidFieldOptions } from './nanoid-field';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export * from './array-field';
 | 
					export * from './array-field';
 | 
				
			||||||
export * from './belongs-to-field';
 | 
					export * from './belongs-to-field';
 | 
				
			||||||
@ -48,6 +49,7 @@ export * from './time-field';
 | 
				
			|||||||
export * from './uid-field';
 | 
					export * from './uid-field';
 | 
				
			||||||
export * from './uuid-field';
 | 
					export * from './uuid-field';
 | 
				
			||||||
export * from './virtual-field';
 | 
					export * from './virtual-field';
 | 
				
			||||||
 | 
					export * from './nanoid-field';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type FieldOptions =
 | 
					export type FieldOptions =
 | 
				
			||||||
  | BaseFieldOptions
 | 
					  | BaseFieldOptions
 | 
				
			||||||
@ -70,6 +72,7 @@ export type FieldOptions =
 | 
				
			|||||||
  | DateFieldOptions
 | 
					  | DateFieldOptions
 | 
				
			||||||
  | UidFieldOptions
 | 
					  | UidFieldOptions
 | 
				
			||||||
  | UUIDFieldOptions
 | 
					  | UUIDFieldOptions
 | 
				
			||||||
 | 
					  | NanoidFieldOptions
 | 
				
			||||||
  | PasswordFieldOptions
 | 
					  | PasswordFieldOptions
 | 
				
			||||||
  | ContextFieldOptions
 | 
					  | ContextFieldOptions
 | 
				
			||||||
  | BelongsToFieldOptions
 | 
					  | BelongsToFieldOptions
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										40
									
								
								packages/core/database/src/fields/nanoid-field.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								packages/core/database/src/fields/nanoid-field.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					import { DataTypes } from 'sequelize';
 | 
				
			||||||
 | 
					import { BaseColumnFieldOptions, Field } from './field';
 | 
				
			||||||
 | 
					import { customAlphabet, nanoid } from 'nanoid';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DEFAULT_SIZE = 12;
 | 
				
			||||||
 | 
					export class NanoidField extends Field {
 | 
				
			||||||
 | 
					  get dataType() {
 | 
				
			||||||
 | 
					    return DataTypes.STRING;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  init() {
 | 
				
			||||||
 | 
					    const { name, size, customAlphabet: customAlphabetOptions } = this.options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.listener = async (instance) => {
 | 
				
			||||||
 | 
					      const value = instance.get(name);
 | 
				
			||||||
 | 
					      if (!value) {
 | 
				
			||||||
 | 
					        const nanoIdFunc = customAlphabetOptions ? customAlphabet(customAlphabetOptions) : nanoid;
 | 
				
			||||||
 | 
					        instance.set(name, nanoIdFunc(size || DEFAULT_SIZE));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bind() {
 | 
				
			||||||
 | 
					    super.bind();
 | 
				
			||||||
 | 
					    this.on('beforeCreate', this.listener);
 | 
				
			||||||
 | 
					    this.on('beforeUpdate', this.listener);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  unbind() {
 | 
				
			||||||
 | 
					    super.unbind();
 | 
				
			||||||
 | 
					    this.off('beforeCreate', this.listener);
 | 
				
			||||||
 | 
					    this.off('beforeUpdate', this.listener);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface NanoidFieldOptions extends BaseColumnFieldOptions {
 | 
				
			||||||
 | 
					  type: 'nanoid';
 | 
				
			||||||
 | 
					  size?: number;
 | 
				
			||||||
 | 
					  customAlphabet?: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,9 +1,10 @@
 | 
				
			|||||||
const postgres = {
 | 
					const postgres = {
 | 
				
			||||||
  'character varying': 'string',
 | 
					  'character varying': ['string', 'uuid', 'nanoid'],
 | 
				
			||||||
  varchar: 'string',
 | 
					  varchar: ['string', 'uuid', 'nanoid'],
 | 
				
			||||||
 | 
					  char: ['string', 'uuid', 'nanoid'],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  character: 'string',
 | 
					  character: 'string',
 | 
				
			||||||
  text: 'text',
 | 
					  text: 'text',
 | 
				
			||||||
  char: 'string',
 | 
					 | 
				
			||||||
  oid: 'string',
 | 
					  oid: 'string',
 | 
				
			||||||
  name: 'string',
 | 
					  name: 'string',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -29,7 +30,7 @@ const postgres = {
 | 
				
			|||||||
  path: 'json',
 | 
					  path: 'json',
 | 
				
			||||||
  polygon: 'json',
 | 
					  polygon: 'json',
 | 
				
			||||||
  circle: 'json',
 | 
					  circle: 'json',
 | 
				
			||||||
  uuid: 'string',
 | 
					  uuid: 'uuid',
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mysql = {
 | 
					const mysql = {
 | 
				
			||||||
@ -41,10 +42,10 @@ const mysql = {
 | 
				
			|||||||
  'tinyint unsigned': ['integer', 'boolean', 'sort'],
 | 
					  'tinyint unsigned': ['integer', 'boolean', 'sort'],
 | 
				
			||||||
  'mediumint unsigned': ['integer', 'boolean', 'sort'],
 | 
					  'mediumint unsigned': ['integer', 'boolean', 'sort'],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  char: 'string',
 | 
					  char: ['string', 'uuid', 'nanoid'],
 | 
				
			||||||
 | 
					  varchar: ['string', 'uuid', 'nanoid'],
 | 
				
			||||||
  date: 'date',
 | 
					  date: 'date',
 | 
				
			||||||
  time: 'time',
 | 
					  time: 'time',
 | 
				
			||||||
  varchar: 'string',
 | 
					 | 
				
			||||||
  text: 'text',
 | 
					  text: 'text',
 | 
				
			||||||
  longtext: 'text',
 | 
					  longtext: 'text',
 | 
				
			||||||
  int: ['integer', 'sort'],
 | 
					  int: ['integer', 'sort'],
 | 
				
			||||||
@ -64,7 +65,7 @@ const mysql = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const sqlite = {
 | 
					const sqlite = {
 | 
				
			||||||
  text: 'text',
 | 
					  text: 'text',
 | 
				
			||||||
  varchar: 'string',
 | 
					  varchar: ['string', 'uuid', 'nanoid'],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  integer: 'integer',
 | 
					  integer: 'integer',
 | 
				
			||||||
  real: 'real',
 | 
					  real: 'real',
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user