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