diff --git a/packages/core/client/src/collection-manager/collectionPlugin.ts b/packages/core/client/src/collection-manager/collectionPlugin.ts index 270393cf9d..8d71266aaa 100644 --- a/packages/core/client/src/collection-manager/collectionPlugin.ts +++ b/packages/core/client/src/collection-manager/collectionPlugin.ts @@ -52,7 +52,6 @@ import { UUIDFieldInterface, NanoidFieldInterface, UnixTimestampFieldInterface, - DateFieldInterface, } from './interfaces'; import { GeneralCollectionTemplate, @@ -174,7 +173,6 @@ export class CollectionPlugin extends Plugin { UUIDFieldInterface, NanoidFieldInterface, UnixTimestampFieldInterface, - DateFieldInterface, ]); } diff --git a/packages/core/client/src/collection-manager/interfaces/components/index.tsx b/packages/core/client/src/collection-manager/interfaces/components/index.tsx index 2fbd571127..ccb5915a60 100644 --- a/packages/core/client/src/collection-manager/interfaces/components/index.tsx +++ b/packages/core/client/src/collection-manager/interfaces/components/index.tsx @@ -7,8 +7,8 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { Switch, Radio, Input } from 'antd'; -import React, { useEffect, useState } from 'react'; +import { Switch } from 'antd'; +import React from 'react'; export const TargetKey = () => { return
Target key
; @@ -50,37 +50,3 @@ export const ForeignKey2 = () => { ); }; - -// 自定义 Radio 组件 -export const CustomRadio = (props) => { - const { options, onChange } = props; - const [value, setValue] = useState(props.value); - useEffect(() => { - setValue(['server', 'client'].includes(props.value) ? props.value : 'custom'); - }, [props.value]); - const handleRadioChange = (e) => { - setValue(e.target.value); - if (e.target.value !== 'custom') { - onChange?.(e.target.value); - } - }; - - return ( - - {options.map((option) => ( - - {option.label} - {option.value === 'custom' && value === 'custom' ? ( - { - onChange?.(e.target.value); - }} - value={['server', 'client', 'custom'].includes(props.value) ? null : props.value} - /> - ) : null} - - ))} - - ); -}; diff --git a/packages/core/client/src/collection-manager/interfaces/date.ts b/packages/core/client/src/collection-manager/interfaces/date.ts deleted file mode 100644 index 59d23be7fa..0000000000 --- a/packages/core/client/src/collection-manager/interfaces/date.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * This file is part of the NocoBase (R) project. - * Copyright (c) 2020-2024 NocoBase Co., Ltd. - * Authors: NocoBase Team. - * - * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. - * For more information, please refer to: https://www.nocobase.com/agreement. - */ - -import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface'; -import { dateTimeProps, defaultProps, operators } from './properties'; - -export class DateFieldInterface extends CollectionFieldInterface { - name = 'date'; - type = 'object'; - group = 'datetime'; - order = 1; - title = '{{t("Date")}}'; - sortable = true; - default = { - type: 'dateOnly', - uiSchema: { - type: 'string', - 'x-component': 'DatePicker', - 'x-component-props': { - dateOnly: true, - }, - }, - }; - availableTypes = ['date', 'dateOnly', 'string']; - hasDefaultValue = true; - properties = { - ...defaultProps, - 'uiSchema.x-component-props.dateFormat': { - type: 'string', - title: '{{t("Date format")}}', - 'x-component': 'Radio.Group', - 'x-decorator': 'FormItem', - default: 'YYYY-MM-DD', - enum: [ - { - label: '{{t("Year/Month/Day")}}', - value: 'YYYY/MM/DD', - }, - { - label: '{{t("Year-Month-Day")}}', - value: 'YYYY-MM-DD', - }, - { - label: '{{t("Day/Month/Year")}}', - value: 'DD/MM/YYYY', - }, - ], - }, - }; - filterable = { - operators: operators.datetime, - }; - titleUsable = true; -} diff --git a/packages/core/client/src/collection-manager/interfaces/datetime.ts b/packages/core/client/src/collection-manager/interfaces/datetime.ts index 3492a87b6a..615d11e9ea 100644 --- a/packages/core/client/src/collection-manager/interfaces/datetime.ts +++ b/packages/core/client/src/collection-manager/interfaces/datetime.ts @@ -19,9 +19,6 @@ export class DatetimeFieldInterface extends CollectionFieldInterface { sortable = true; default = { type: 'date', - defaultToCurrentTime: false, - onUpdateToCurrentTime: false, - timezone: 'server', uiSchema: { type: 'string', 'x-component': 'DatePicker', diff --git a/packages/core/client/src/collection-manager/interfaces/index.ts b/packages/core/client/src/collection-manager/interfaces/index.ts index 663f55ac2d..6778d83413 100644 --- a/packages/core/client/src/collection-manager/interfaces/index.ts +++ b/packages/core/client/src/collection-manager/interfaces/index.ts @@ -46,4 +46,3 @@ export * from './sort'; export * from './uuid'; export * from './nanoid'; export * from './unixTimestamp'; -export * from './date'; diff --git a/packages/core/client/src/collection-manager/interfaces/properties/index.ts b/packages/core/client/src/collection-manager/interfaces/properties/index.ts index df63fd215b..1ef36e4b61 100644 --- a/packages/core/client/src/collection-manager/interfaces/properties/index.ts +++ b/packages/core/client/src/collection-manager/interfaces/properties/index.ts @@ -10,7 +10,6 @@ import { Field } from '@formily/core'; import { ISchema } from '@formily/react'; import { uid } from '@formily/shared'; -import { CustomRadio } from '../components'; export * as operators from './operators'; export const type: ISchema = { @@ -226,29 +225,6 @@ export const reverseFieldProperties: Record = { }; export const dateTimeProps: { [key: string]: ISchema } = { - timezone: { - type: 'string', - title: '{{t("Timezone")}}', - 'x-component': CustomRadio, - 'x-decorator': 'FormItem', - default: 'server', - 'x-component-props': { - options: [ - { - label: '{{t("None")}}', - value: 'server', - }, - { - label: '{{t("Client\'s time zone")}}', - value: 'client', - }, - { - label: '{{t("Custom")}}', - value: 'custom', - }, - ], - }, - }, 'uiSchema.x-component-props.dateFormat': { type: 'string', title: '{{t("Date format")}}', @@ -277,10 +253,10 @@ export const dateTimeProps: { [key: string]: ISchema } = { 'x-content': '{{t("Show time")}}', 'x-reactions': [ `{{(field) => { - field.query('..[].timeFormat').take(f => { - f.display = field.value ? 'visible' : 'none'; - }); - }}}`, + field.query('..[].timeFormat').take(f => { + f.display = field.value ? 'visible' : 'none'; + }); + }}}`, ], }, 'uiSchema.x-component-props.timeFormat': { @@ -300,18 +276,6 @@ export const dateTimeProps: { [key: string]: ISchema } = { }, ], }, - defaultToCurrentTime: { - type: 'boolean', - 'x-decorator': 'FormItem', - 'x-component': 'Checkbox', - 'x-content': '{{t("Default value to current time")}}', - }, - onUpdateToCurrentTime: { - type: 'boolean', - 'x-decorator': 'FormItem', - 'x-component': 'Checkbox', - 'x-content': '{{t("Automatically update timestamp on update")}}', - }, }; export const dataSource: ISchema = { diff --git a/packages/core/client/src/collection-manager/interfaces/unixTimestamp.tsx b/packages/core/client/src/collection-manager/interfaces/unixTimestamp.tsx index 8e9db2b6d1..47b3ebc2cf 100644 --- a/packages/core/client/src/collection-manager/interfaces/unixTimestamp.tsx +++ b/packages/core/client/src/collection-manager/interfaces/unixTimestamp.tsx @@ -8,8 +8,8 @@ */ import { CollectionFieldInterface } from '../../data-source/collection-field-interface/CollectionFieldInterface'; -import { defaultProps, operators } from './properties'; -import { CustomRadio } from './components'; +import { dateTimeProps, defaultProps, operators } from './properties'; + export class UnixTimestampFieldInterface extends CollectionFieldInterface { name = 'unixTimestamp'; type = 'object'; @@ -18,47 +18,21 @@ export class UnixTimestampFieldInterface extends CollectionFieldInterface { title = '{{t("Unix Timestamp")}}'; sortable = true; default = { - type: 'unixTimestamp', - accuracy: 'second', - timezone: 'server', - defaultToCurrentTime: false, - onUpdateToCurrentTime: false, + type: 'bigInt', uiSchema: { type: 'number', 'x-component': 'UnixTimestamp', 'x-component-props': { + accuracy: 'second', showTime: true, }, }, }; - availableTypes = ['integer', 'bigInt', 'unixTimestamp']; - hasDefaultValue = false; + availableTypes = ['integer', 'bigInt']; + hasDefaultValue = true; properties = { ...defaultProps, - timezone: { - type: 'string', - title: '{{t("Timezone")}}', - 'x-component': CustomRadio, - 'x-decorator': 'FormItem', - default: 'server', - 'x-component-props': { - options: [ - { - label: '{{t("None")}}', - value: 'server', - }, - { - label: '{{t("Client\'s time zone")}}', - value: 'client', - }, - { - label: 'custom', - value: 'custom', - }, - ], - }, - }, - accuracy: { + 'uiSchema.x-component-props.accuracy': { type: 'string', title: '{{t("Accuracy")}}', 'x-component': 'Radio.Group', @@ -69,20 +43,6 @@ export class UnixTimestampFieldInterface extends CollectionFieldInterface { { value: 'second', label: '{{t("Second")}}' }, ], }, - defaultToCurrentTime: { - type: 'boolean', - 'x-decorator': 'FormItem', - 'x-component': 'Checkbox', - 'x-content': '{{t("Default value to current time")}}', - default: true, - }, - onUpdateToCurrentTime: { - type: 'boolean', - 'x-decorator': 'FormItem', - 'x-component': 'Checkbox', - 'x-content': '{{t("Automatically update timestamp on update")}}', - default: true, - }, }; filterable = { operators: operators.number, diff --git a/packages/core/client/src/locale/zh-CN.json b/packages/core/client/src/locale/zh-CN.json index 484c587e2a..9162e64e24 100644 --- a/packages/core/client/src/locale/zh-CN.json +++ b/packages/core/client/src/locale/zh-CN.json @@ -283,7 +283,7 @@ "Checkbox group": "复选框", "China region": "中国行政区", "Date & Time": "日期 & 时间", - "Datetime": "日期时间", + "Datetime": "日期", "Relation": "关系类型", "Link to": "关联", "Link to description": "用于快速创建表关系,可兼容大多数普通场景。适合非开发人员使用。作为字段存在时,它是一个下拉选择用于选择目标数据表的数据。创建后,将同时在目标数据表中生成当前数据表的关联字段。", @@ -967,8 +967,5 @@ "Clear default value": "清除默认值", "Open in new window": "新窗口打开", "Sorry, the page you visited does not exist.": "抱歉,你访问的页面不存在。", - "Template engine": "模板引擎", - "Default value to current time":"设置字段默认值为当前时间", - "Automatically update timestamp on update":"当记录更新时自动设置字段值为当前时间", - "Client's time zone":"客户端时区" + "Set Template Engine": "设置模板引擎" } diff --git a/packages/core/client/src/schema-component/antd/date-picker/util.ts b/packages/core/client/src/schema-component/antd/date-picker/util.ts index 9c80b91a28..59026b3efb 100644 --- a/packages/core/client/src/schema-component/antd/date-picker/util.ts +++ b/packages/core/client/src/schema-component/antd/date-picker/util.ts @@ -78,20 +78,17 @@ export const mapDatePicker = function () { return (props: any) => { const format = getDefaultFormat(props) as any; const onChange = props.onChange; + return { ...props, format: format, value: str2moment(props.value, props), - onChange: (value: Dayjs | null, dateString) => { + onChange: (value: Dayjs | null) => { if (onChange) { if (!props.showTime && value) { value = value.startOf('day'); } - if (props.dateOnly) { - onChange(dateString); - } else { - onChange(moment2str(value, props)); - } + onChange(moment2str(value, props)); } }, }; diff --git a/packages/core/client/src/schema-component/antd/unix-timestamp/UnixTimestamp.tsx b/packages/core/client/src/schema-component/antd/unix-timestamp/UnixTimestamp.tsx index e83f9d00bc..5d19a1e118 100644 --- a/packages/core/client/src/schema-component/antd/unix-timestamp/UnixTimestamp.tsx +++ b/packages/core/client/src/schema-component/antd/unix-timestamp/UnixTimestamp.tsx @@ -8,32 +8,57 @@ */ import { connect, mapReadPretty } from '@formily/react'; -import React from '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(); // 默认返回毫秒级时间戳 + } +} interface UnixTimestampProps { - value?: any; + value?: number; + accuracy?: 'millisecond' | 'second'; onChange?: (value: number) => void; } export const UnixTimestamp = connect( (props: UnixTimestampProps) => { - const { value, onChange } = props; - + const { value, onChange, accuracy = 'second' } = props; + const v = useMemo(() => toValue(value, accuracy), [value, accuracy]); return ( { if (onChange) { - onChange(v); + onChange(getTimestamp(v, accuracy)); } }} /> ); }, mapReadPretty((props) => { - const { value } = props; - return ; + const { value, accuracy = 'second' } = props; + const v = useMemo(() => toValue(value, accuracy), [value, accuracy]); + return ; }), ); diff --git a/packages/core/client/src/schema-component/antd/unix-timestamp/__tests__/UnixTimestamp.test.tsx b/packages/core/client/src/schema-component/antd/unix-timestamp/__tests__/UnixTimestamp.test.tsx index c9c107f45c..ba82c73147 100644 --- a/packages/core/client/src/schema-component/antd/unix-timestamp/__tests__/UnixTimestamp.test.tsx +++ b/packages/core/client/src/schema-component/antd/unix-timestamp/__tests__/UnixTimestamp.test.tsx @@ -13,9 +13,11 @@ import { UnixTimestamp } from '@nocobase/client'; describe('UnixTimestamp', () => { it('renders without errors', async () => { const { container } = await renderAppOptions({ - Component: UnixTimestamp as any, - props: {}, - value: null, + Component: UnixTimestamp, + props: { + accuracy: 'millisecond', + }, + value: 0, }); expect(container).toMatchInlineSnapshot(`
@@ -67,10 +69,78 @@ describe('UnixTimestamp', () => { `); }); + it('millisecond', async () => { + await renderAppOptions({ + Component: UnixTimestamp, + value: 1712819630000, + props: { + accuracy: 'millisecond', + }, + }); + await waitFor(() => { + expect(screen.getByRole('textbox')).toHaveValue('2024-04-11'); + }); + }); + + it('second', async () => { + await renderAppOptions({ + Component: UnixTimestamp, + value: 1712819630, + props: { + accuracy: 'second', + }, + }); + + await waitFor(() => { + expect(screen.getByRole('textbox')).toHaveValue('2024-04-11'); + }); + }); + + it('string', async () => { + await renderAppOptions({ + Component: UnixTimestamp, + value: '2024-04-11', + props: { + accuracy: 'millisecond', + }, + }); + + await waitFor(() => { + expect(screen.getByRole('textbox')).toHaveValue('2024-04-11'); + }); + }); + + it('change', async () => { + const onChange = vitest.fn(); + await renderAppOptions({ + Component: UnixTimestamp, + value: '2024-04-11', + onChange, + props: { + accuracy: 'millisecond', + }, + }); + await userEvent.click(screen.getByRole('textbox')); + + await waitFor(() => { + expect(screen.queryByRole('table')).toBeInTheDocument(); + }); + + await userEvent.click(document.querySelector('td[title="2024-04-12"]')); + + await waitFor(() => { + expect(screen.getByRole('textbox')).toHaveValue('2024-04-12'); + }); + expect(onChange).toBeCalledWith(1712880000000); + }); + it('read pretty', async () => { const { container } = await renderReadPrettyApp({ - Component: UnixTimestamp as any, + Component: UnixTimestamp, value: '2024-04-11', + props: { + accuracy: 'millisecond', + }, }); expect(screen.getByText('2024-04-11')).toBeInTheDocument(); diff --git a/packages/core/database/src/__tests__/fields/date-only.test.ts b/packages/core/database/src/__tests__/fields/date-only.test.ts deleted file mode 100644 index 1b6d9a60f7..0000000000 --- a/packages/core/database/src/__tests__/fields/date-only.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * This file is part of the NocoBase (R) project. - * Copyright (c) 2020-2024 NocoBase Co., Ltd. - * Authors: NocoBase Team. - * - * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. - * For more information, please refer to: https://www.nocobase.com/agreement. - */ - -import { Database, mockDatabase } from '@nocobase/database'; - -describe('date only', () => { - let db: Database; - - beforeEach(async () => { - db = mockDatabase({ - timezone: '+08:00', - }); - await db.clean({ drop: true }); - }); - - afterEach(async () => { - await db.close(); - }); - - it('should set date field with dateOnly', async () => { - db.collection({ - name: 'tests', - fields: [{ name: 'date1', type: 'dateOnly' }], - }); - - await db.sync(); - - const item = await db.getRepository('tests').create({ - values: { - date1: '2023-03-24', - }, - }); - - expect(item.get('date1')).toBe('2023-03-24'); - }); -}); diff --git a/packages/core/database/src/__tests__/fields/date.test.ts b/packages/core/database/src/__tests__/fields/date.test.ts index 4c6b373721..e1a2ff189e 100644 --- a/packages/core/database/src/__tests__/fields/date.test.ts +++ b/packages/core/database/src/__tests__/fields/date.test.ts @@ -11,69 +11,6 @@ import { mockDatabase } from '../'; import { Database } from '../../database'; import { Repository } from '../../repository'; -describe('timezone', () => { - let db: Database; - - beforeEach(async () => { - db = mockDatabase({ - timezone: '+08:00', - }); - await db.clean({ drop: true }); - }); - - afterEach(async () => { - await db.close(); - }); - - describe('timezone', () => { - test('custom', async () => { - db.collection({ - name: 'tests', - timestamps: false, - fields: [{ name: 'date1', type: 'date', timezone: '+06:00' }], - }); - - await db.sync(); - const repository = db.getRepository('tests'); - const instance = await repository.create({ values: { date1: '2023-03-24 00:00:00' } }); - const date1 = instance.get('date1'); - expect(date1.toISOString()).toEqual('2023-03-23T18:00:00.000Z'); - }); - - test('client', async () => { - db.collection({ - name: 'tests', - timestamps: false, - fields: [{ name: 'date1', type: 'date', timezone: 'client' }], - }); - - await db.sync(); - const repository = db.getRepository('tests'); - const instance = await repository.create({ - values: { date1: '2023-03-24 01:00:00' }, - context: { - timezone: '+01:00', - }, - }); - const date1 = instance.get('date1'); - expect(date1.toISOString()).toEqual('2023-03-24T00:00:00.000Z'); - }); - - test('server', async () => { - db.collection({ - name: 'tests', - fields: [{ name: 'date1', type: 'date', timezone: 'server' }], - }); - - await db.sync(); - const repository = db.getRepository('tests'); - const instance = await repository.create({ values: { date1: '2023-03-24 08:00:00' } }); - const date1 = instance.get('date1'); - expect(date1.toISOString()).toEqual('2023-03-24T00:00:00.000Z'); - }); - }); -}); - describe('date-field', () => { let db: Database; let repository: Repository; @@ -93,80 +30,16 @@ describe('date-field', () => { await db.close(); }); - it('should set default to current time', async () => { - const c1 = db.collection({ - name: 'test11', - fields: [ - { - name: 'date1', - type: 'date', - defaultToCurrentTime: true, - }, - ], - }); - - await db.sync(); - - const instance = await c1.repository.create({}); - const date1 = instance.get('date1'); - expect(date1).toBeDefined(); - }); - - it('should set to current time when update', async () => { - const c1 = db.collection({ - name: 'test11', - fields: [ - { - name: 'date1', - type: 'date', - onUpdateToCurrentTime: true, - }, - { - name: 'title', - type: 'string', - }, - ], - }); - - await db.sync(); - - const instance = await c1.repository.create({ + const createExpectToBe = async (key, actual, expected) => { + const instance = await repository.create({ values: { - title: 'test', + [key]: actual, }, }); - - const date1Val = instance.get('date1'); - expect(date1Val).toBeDefined(); - - console.log('update'); - await c1.repository.update({ - values: { - title: 'test2', - }, - filter: { - id: instance.get('id'), - }, - }); - - await instance.reload(); - - const date1Val2 = instance.get('date1'); - expect(date1Val2).toBeDefined(); - - expect(date1Val2.getTime()).toBeGreaterThan(date1Val.getTime()); - }); + return expect(instance.get(key).toISOString()).toEqual(expected); + }; test('create', async () => { - const createExpectToBe = async (key, actual, expected) => { - const instance = await repository.create({ - values: { - [key]: actual, - }, - }); - return expect(instance.get(key).toISOString()).toEqual(expected); - }; - // sqlite 时区不能自定义,只有 +00:00,postgres 和 mysql 可以自定义 DB_TIMEZONE await createExpectToBe('date1', '2023-03-24', '2023-03-24T00:00:00.000Z'); await createExpectToBe('date1', '2023-03-24T16:00:00.000Z', '2023-03-24T16:00:00.000Z'); diff --git a/packages/core/database/src/__tests__/fields/unix-timestamp-field.tests.ts b/packages/core/database/src/__tests__/fields/unix-timestamp-field.tests.ts deleted file mode 100644 index d590653811..0000000000 --- a/packages/core/database/src/__tests__/fields/unix-timestamp-field.tests.ts +++ /dev/null @@ -1,86 +0,0 @@ -/** - * This file is part of the NocoBase (R) project. - * Copyright (c) 2020-2024 NocoBase Co., Ltd. - * Authors: NocoBase Team. - * - * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. - * For more information, please refer to: https://www.nocobase.com/agreement. - */ - -import { Database, mockDatabase } from '@nocobase/database'; -import moment from 'moment'; - -describe('unix timestamp field', () => { - let db: Database; - - beforeEach(async () => { - db = mockDatabase(); - await db.clean({ drop: true }); - }); - - afterEach(async () => { - await db.close(); - }); - - it('should set default to current time', async () => { - const c1 = db.collection({ - name: 'test11', - fields: [ - { - name: 'date1', - type: 'unixTimestamp', - defaultToCurrentTime: true, - }, - ], - }); - - await db.sync(); - - const instance = await c1.repository.create({}); - const date1 = instance.get('date1'); - expect(date1).toBeDefined(); - - console.log(instance.toJSON()); - }); - - it('should set date value', async () => { - const c1 = db.collection({ - name: 'test12', - fields: [ - { - name: 'date1', - type: 'unixTimestamp', - }, - ], - }); - - await db.sync(); - - await c1.repository.create({ - values: { - date1: '2021-01-01T00:00:00Z', - }, - }); - - const item = await c1.repository.findOne(); - const val = item.get('date1'); - const date = moment(val).utc().format('YYYY-MM-DD HH:mm:ss'); - expect(date).toBe('2021-01-01 00:00:00'); - }); - - describe('timezone', () => { - test('custom', async () => { - db.collection({ - name: 'tests', - timestamps: false, - fields: [{ name: 'date1', type: 'unixTimestamp', timezone: '+06:00' }], - }); - - await db.sync(); - const repository = db.getRepository('tests'); - const instance = await repository.create({ values: { date1: '2023-03-24 00:00:00' } }); - const date1 = instance.get('date1'); - expect(date1.toISOString()).toEqual('2023-03-23T18:00:00.000Z'); - }); - }); -}); diff --git a/packages/core/database/src/database.ts b/packages/core/database/src/database.ts index bf10c3bf17..6300eb55bf 100644 --- a/packages/core/database/src/database.ts +++ b/packages/core/database/src/database.ts @@ -34,6 +34,7 @@ import { import { SequelizeStorage, Umzug } from 'umzug'; import { Collection, CollectionOptions, RepositoryType } from './collection'; import { CollectionFactory } from './collection-factory'; +import { CollectionGroupManager } from './collection-group-manager'; import { ImporterReader, ImportFileExtension } from './collection-importer'; import DatabaseUtils from './database-utils'; import ReferencesMap from './features/references-map'; @@ -41,6 +42,7 @@ import { referentialIntegrityCheck } from './features/referential-integrity-chec import { ArrayFieldRepository } from './field-repository/array-field-repository'; import * as FieldTypes from './fields'; import { Field, FieldContext, RelationField } from './fields'; +import { checkDatabaseVersion } from './helpers'; import { InheritedCollection } from './inherited-collection'; import InheritanceMap from './inherited-map'; import { InterfaceManager } from './interface-manager'; @@ -219,9 +221,6 @@ export class Database extends EventEmitter implements AsyncEmitter { } } - // @ts-ignore - opts.rawTimezone = opts.timezone; - if (options.dialect === 'sqlite') { delete opts.timezone; } else if (!opts.timezone) { @@ -849,8 +848,7 @@ export class Database extends EventEmitter implements AsyncEmitter { * @internal */ async checkVersion() { - return true; - // return await checkDatabaseVersion(this); + return await checkDatabaseVersion(this); } /** diff --git a/packages/core/database/src/fields/date-field.ts b/packages/core/database/src/fields/date-field.ts index 8cae2221d5..f40b27de3e 100644 --- a/packages/core/database/src/fields/date-field.ts +++ b/packages/core/database/src/fields/date-field.ts @@ -10,14 +10,8 @@ import { DataTypes } from 'sequelize'; import { BaseColumnFieldOptions, Field } from './field'; -const datetimeRegex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/; - -function isValidDatetime(str) { - return datetimeRegex.test(str); -} - export class DateField extends Field { - get dataType(): any { + get dataType() { return DataTypes.DATE(3); } @@ -39,59 +33,6 @@ export class DateField extends Field { return props.gmt; } - init() { - const { name, defaultToCurrentTime, onUpdateToCurrentTime, timezone } = this.options; - - this.resolveTimeZone = (context) => { - // @ts-ignore - const serverTimeZone = this.database.options.rawTimezone; - if (timezone === 'server') { - return serverTimeZone; - } - - if (timezone === 'client') { - return context?.timezone || serverTimeZone; - } - - if (timezone) { - return timezone; - } - - return serverTimeZone; - }; - - this.beforeSave = async (instance, options) => { - const value = instance.get(name); - - if (!value && instance.isNewRecord && defaultToCurrentTime) { - instance.set(name, new Date()); - return; - } - - if (onUpdateToCurrentTime) { - instance.set(name, new Date()); - return; - } - }; - } - - setter(value, options) { - if (value === null) { - return value; - } - if (value instanceof Date) { - return value; - } - - if (typeof value === 'string' && isValidDatetime(value)) { - const dateTimezone = this.resolveTimeZone(options?.context); - const dateString = `${value} ${dateTimezone}`; - return new Date(dateString); - } - - return value; - } - bind() { super.bind(); @@ -110,13 +51,6 @@ export class DateField extends Field { // @ts-ignore model.refreshAttributes(); } - - this.on('beforeSave', this.beforeSave); - } - - unbind() { - super.unbind(); - this.off('beforeSave', this.beforeSave); } } diff --git a/packages/core/database/src/fields/date-only-field.ts b/packages/core/database/src/fields/date-only-field.ts deleted file mode 100644 index 5fce9d1b74..0000000000 --- a/packages/core/database/src/fields/date-only-field.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * This file is part of the NocoBase (R) project. - * Copyright (c) 2020-2024 NocoBase Co., Ltd. - * Authors: NocoBase Team. - * - * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. - * For more information, please refer to: https://www.nocobase.com/agreement. - */ - -import { BaseColumnFieldOptions, Field } from './field'; -import { DataTypes } from 'sequelize'; - -export class DateOnlyField extends Field { - get dataType(): any { - return DataTypes.DATEONLY; - } -} - -export interface DateOnlyFieldOptions extends BaseColumnFieldOptions { - type: 'dateOnly'; -} diff --git a/packages/core/database/src/fields/field.ts b/packages/core/database/src/fields/field.ts index e4b0b70024..b9f230dd4f 100644 --- a/packages/core/database/src/fields/field.ts +++ b/packages/core/database/src/fields/field.ts @@ -56,7 +56,7 @@ export abstract class Field { return this.options.type; } - abstract get dataType(): any; + abstract get dataType(); isRelationField() { return false; @@ -171,13 +171,11 @@ export abstract class Field { Object.assign(opts, { type: this.database.sequelize.normalizeDataType(this.dataType) }); } - Object.assign(opts, this.additionalSequelizeOptions()); - return opts; } - additionalSequelizeOptions() { - return {}; + isSqlite() { + return this.database.sequelize.getDialect() === 'sqlite'; } typeToString() { diff --git a/packages/core/database/src/fields/index.ts b/packages/core/database/src/fields/index.ts index d698e44cd6..610b6f1ad1 100644 --- a/packages/core/database/src/fields/index.ts +++ b/packages/core/database/src/fields/index.ts @@ -36,8 +36,6 @@ import { UUIDFieldOptions } from './uuid-field'; import { VirtualFieldOptions } from './virtual-field'; import { NanoidFieldOptions } from './nanoid-field'; import { EncryptionField } from './encryption-field'; -import { UnixTimestampFieldOptions } from './unix-timestamp-field'; -import { DateOnlyFieldOptions } from './date-only-field'; export * from './array-field'; export * from './belongs-to-field'; @@ -45,7 +43,6 @@ export * from './belongs-to-many-field'; export * from './boolean-field'; export * from './context-field'; export * from './date-field'; -export * from './date-only-field'; export * from './field'; export * from './has-many-field'; export * from './has-one-field'; @@ -64,7 +61,6 @@ export * from './uuid-field'; export * from './virtual-field'; export * from './nanoid-field'; export * from './encryption-field'; -export * from './unix-timestamp-field'; export type FieldOptions = | BaseFieldOptions @@ -85,8 +81,6 @@ export type FieldOptions = | SetFieldOptions | TimeFieldOptions | DateFieldOptions - | DateOnlyFieldOptions - | UnixTimestampFieldOptions | UidFieldOptions | UUIDFieldOptions | NanoidFieldOptions diff --git a/packages/core/database/src/fields/unix-timestamp-field.ts b/packages/core/database/src/fields/unix-timestamp-field.ts deleted file mode 100644 index fc634ecde5..0000000000 --- a/packages/core/database/src/fields/unix-timestamp-field.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * This file is part of the NocoBase (R) project. - * Copyright (c) 2020-2024 NocoBase Co., Ltd. - * Authors: NocoBase Team. - * - * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. - * For more information, please refer to: https://www.nocobase.com/agreement. - */ - -import { DataTypes } from 'sequelize'; -import { DateField } from './date-field'; -import { BaseColumnFieldOptions } from './field'; - -export class UnixTimestampField extends DateField { - get dataType() { - return DataTypes.BIGINT; - } - - additionalSequelizeOptions(): {} { - const { name } = this.options; - let { accuracy } = this.options; - - if (this.options?.uiSchema['x-component-props']?.accuracy) { - accuracy = this.options?.uiSchema['x-component-props']?.accuracy; - } - - if (!accuracy) { - accuracy = 'second'; - } - - let rationalNumber = 1000; - - if (accuracy === 'millisecond') { - rationalNumber = 1; - } - - return { - get() { - const value = this.getDataValue(name); - if (value === null || value === undefined) { - return value; - } - - return new Date(value * rationalNumber); - }, - set(value) { - if (value === null || value === undefined) { - this.setDataValue(name, value); - } else { - // date to unix timestamp - this.setDataValue(name, Math.floor(new Date(value).getTime() / rationalNumber)); - } - }, - }; - } -} - -export interface UnixTimestampFieldOptions extends BaseColumnFieldOptions { - type: 'unix-timestamp'; -} diff --git a/packages/core/database/src/model.ts b/packages/core/database/src/model.ts index 9ec745fcca..9152ae6b39 100644 --- a/packages/core/database/src/model.ts +++ b/packages/core/database/src/model.ts @@ -7,7 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import lodash from 'lodash'; +import lodash, { isPlainObject } from 'lodash'; import { Model as SequelizeModel, ModelStatic } from 'sequelize'; import { Collection } from './collection'; import { Database } from './database'; @@ -50,21 +50,6 @@ export class Model(values, { ...options, @@ -645,7 +645,7 @@ export class Repository exten * @internal */ public perfHistograms = new Map(); - /** - * @internal - */ - public syncManager: SyncManager; - public requestLogger: Logger; protected plugins = new Map(); protected _appSupervisor: AppSupervisor = AppSupervisor.getInstance(); + protected _started: Date | null = null; private _authenticated = false; private _maintaining = false; private _maintainingCommandStatus: MaintainingCommandStatus; private _maintainingStatusBeforeCommand: MaintainingCommandStatus | null; private _actionCommand: Command; + + /** + * @internal + */ + public syncManager: SyncManager; + public requestLogger: Logger; private sqlLogger: Logger; + protected _logger: SystemLogger; constructor(public options: ApplicationOptions) { super(); @@ -238,8 +241,6 @@ export class Application exten } } - protected _started: Date | null = null; - /** * @experimental */ @@ -247,8 +248,6 @@ export class Application exten return this._started; } - protected _logger: SystemLogger; - get logger() { return this._logger; }