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,
 | 
			
		||||
  UrlFieldInterface,
 | 
			
		||||
  SortFieldInterface,
 | 
			
		||||
  UUIDFieldInterface,
 | 
			
		||||
  NanoidFieldInterface,
 | 
			
		||||
  UnixTimestampFieldInterface,
 | 
			
		||||
} from './interfaces';
 | 
			
		||||
import {
 | 
			
		||||
  GeneralCollectionTemplate,
 | 
			
		||||
@ -155,6 +158,9 @@ export class CollectionPlugin extends Plugin {
 | 
			
		||||
      UpdatedByFieldInterface,
 | 
			
		||||
      UrlFieldInterface,
 | 
			
		||||
      SortFieldInterface,
 | 
			
		||||
      UUIDFieldInterface,
 | 
			
		||||
      NanoidFieldInterface,
 | 
			
		||||
      UnixTimestampFieldInterface,
 | 
			
		||||
    ]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -34,3 +34,6 @@ export * from './updatedAt';
 | 
			
		||||
export * from './updatedBy';
 | 
			
		||||
export * from './url';
 | 
			
		||||
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": "负责人",
 | 
			
		||||
  "Plugin settings": "插件设置",
 | 
			
		||||
  "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) => {
 | 
			
		||||
  const { utc = true } = useDatePickerContext();
 | 
			
		||||
  const value = Array.isArray(props.value) ? props.value[0] : props.value;
 | 
			
		||||
  console.log(value);
 | 
			
		||||
  props = { utc, ...props };
 | 
			
		||||
  return <InternalDatePicker {...props} value={value} />;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -50,5 +50,7 @@ export * from './time-picker';
 | 
			
		||||
export * from './tree-select';
 | 
			
		||||
export * from './upload';
 | 
			
		||||
export * from './variable';
 | 
			
		||||
export * from './unixTimestamp';
 | 
			
		||||
export * from './nanoIDInput';
 | 
			
		||||
 | 
			
		||||
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(
 | 
			
		||||
  AntdRadio.Group,
 | 
			
		||||
  mapProps({
 | 
			
		||||
  mapProps(
 | 
			
		||||
    {
 | 
			
		||||
      dataSource: 'options',
 | 
			
		||||
  }),
 | 
			
		||||
    },
 | 
			
		||||
    (props) => {
 | 
			
		||||
      return {
 | 
			
		||||
        ...props,
 | 
			
		||||
        value: props.value && typeof props.value !== 'boolean' ? props.value.toString() : props.value,
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
  ),
 | 
			
		||||
  mapReadPretty((props) => {
 | 
			
		||||
    if (!isValid(props.value)) {
 | 
			
		||||
      return <div></div>;
 | 
			
		||||
 | 
			
		||||
@ -9,11 +9,11 @@ import React from 'react';
 | 
			
		||||
const options = [
 | 
			
		||||
  {
 | 
			
		||||
    label: '男',
 | 
			
		||||
    value: 1,
 | 
			
		||||
    value: '1',
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: '女',
 | 
			
		||||
    value: 2,
 | 
			
		||||
    value: '2',
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,12 +8,12 @@ import React from 'react';
 | 
			
		||||
const options = [
 | 
			
		||||
  {
 | 
			
		||||
    label: '男',
 | 
			
		||||
    value: 1,
 | 
			
		||||
    value: '1',
 | 
			
		||||
    color: 'blue',
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: '女',
 | 
			
		||||
    value: 2,
 | 
			
		||||
    value: '2',
 | 
			
		||||
    color: 'red',
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@ -122,7 +122,7 @@ const InternalSelect = connect(
 | 
			
		||||
        }
 | 
			
		||||
        return undefined;
 | 
			
		||||
      }
 | 
			
		||||
      return v;
 | 
			
		||||
      return v ? v.toString() : v;
 | 
			
		||||
    };
 | 
			
		||||
    return (
 | 
			
		||||
      <AntdSelect
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,10 @@ export function getCurrentOptions(values: string | string[], dataSource: any[],
 | 
			
		||||
    if (!options) return [];
 | 
			
		||||
    const current: Option[] = [];
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
    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 { subTablePopoverComponentFieldSettings } from '../modules/fields/component/SubTable/subTablePopoverComponentFieldSettings';
 | 
			
		||||
import { tagComponentFieldSettings } from '../modules/fields/component/Tag/tagComponentFieldSettings';
 | 
			
		||||
import { unixTimestampComponentFieldSettings } from '../modules/fields/component/UnixTimestamp/unixTimestampComponentFieldSettings';
 | 
			
		||||
 | 
			
		||||
export class SchemaSettingsPlugin extends Plugin {
 | 
			
		||||
  async load() {
 | 
			
		||||
@ -90,6 +91,8 @@ export class SchemaSettingsPlugin extends Plugin {
 | 
			
		||||
    this.schemaSettingsManager.add(subformPopoverComponentFieldSettings);
 | 
			
		||||
    this.schemaSettingsManager.add(subTablePopoverComponentFieldSettings);
 | 
			
		||||
    this.schemaSettingsManager.add(datePickerComponentFieldSettings);
 | 
			
		||||
    this.schemaSettingsManager.add(unixTimestampComponentFieldSettings);
 | 
			
		||||
 | 
			
		||||
    this.schemaSettingsManager.add(fileManagerComponentFieldSettings);
 | 
			
		||||
    this.schemaSettingsManager.add(tagComponentFieldSettings);
 | 
			
		||||
    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 { UUIDFieldOptions } from './uuid-field';
 | 
			
		||||
import { VirtualFieldOptions } from './virtual-field';
 | 
			
		||||
import { NanoidFieldOptions } from './nanoid-field';
 | 
			
		||||
 | 
			
		||||
export * from './array-field';
 | 
			
		||||
export * from './belongs-to-field';
 | 
			
		||||
@ -48,6 +49,7 @@ export * from './time-field';
 | 
			
		||||
export * from './uid-field';
 | 
			
		||||
export * from './uuid-field';
 | 
			
		||||
export * from './virtual-field';
 | 
			
		||||
export * from './nanoid-field';
 | 
			
		||||
 | 
			
		||||
export type FieldOptions =
 | 
			
		||||
  | BaseFieldOptions
 | 
			
		||||
@ -70,6 +72,7 @@ export type FieldOptions =
 | 
			
		||||
  | DateFieldOptions
 | 
			
		||||
  | UidFieldOptions
 | 
			
		||||
  | UUIDFieldOptions
 | 
			
		||||
  | NanoidFieldOptions
 | 
			
		||||
  | PasswordFieldOptions
 | 
			
		||||
  | ContextFieldOptions
 | 
			
		||||
  | 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 = {
 | 
			
		||||
  'character varying': 'string',
 | 
			
		||||
  varchar: 'string',
 | 
			
		||||
  'character varying': ['string', 'uuid', 'nanoid'],
 | 
			
		||||
  varchar: ['string', 'uuid', 'nanoid'],
 | 
			
		||||
  char: ['string', 'uuid', 'nanoid'],
 | 
			
		||||
 | 
			
		||||
  character: 'string',
 | 
			
		||||
  text: 'text',
 | 
			
		||||
  char: 'string',
 | 
			
		||||
  oid: 'string',
 | 
			
		||||
  name: 'string',
 | 
			
		||||
 | 
			
		||||
@ -29,7 +30,7 @@ const postgres = {
 | 
			
		||||
  path: 'json',
 | 
			
		||||
  polygon: 'json',
 | 
			
		||||
  circle: 'json',
 | 
			
		||||
  uuid: 'string',
 | 
			
		||||
  uuid: 'uuid',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const mysql = {
 | 
			
		||||
@ -41,10 +42,10 @@ const mysql = {
 | 
			
		||||
  'tinyint unsigned': ['integer', 'boolean', 'sort'],
 | 
			
		||||
  'mediumint unsigned': ['integer', 'boolean', 'sort'],
 | 
			
		||||
 | 
			
		||||
  char: 'string',
 | 
			
		||||
  char: ['string', 'uuid', 'nanoid'],
 | 
			
		||||
  varchar: ['string', 'uuid', 'nanoid'],
 | 
			
		||||
  date: 'date',
 | 
			
		||||
  time: 'time',
 | 
			
		||||
  varchar: 'string',
 | 
			
		||||
  text: 'text',
 | 
			
		||||
  longtext: 'text',
 | 
			
		||||
  int: ['integer', 'sort'],
 | 
			
		||||
@ -64,7 +65,7 @@ const mysql = {
 | 
			
		||||
 | 
			
		||||
const sqlite = {
 | 
			
		||||
  text: 'text',
 | 
			
		||||
  varchar: 'string',
 | 
			
		||||
  varchar: ['string', 'uuid', 'nanoid'],
 | 
			
		||||
 | 
			
		||||
  integer: 'integer',
 | 
			
		||||
  real: 'real',
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user