mirror of
https://gitee.com/nocobase/nocobase.git
synced 2024-11-30 03:08:31 +08:00
feat: support JSONB (#2321)
* chore: yarn.lock * feat: add jsonb option in field drawer * feat: only postgres can use JSONB * chore: add test * refactor: make better * fix: fix build * fix: fix build * fix: should disable JSONB on editing field
This commit is contained in:
parent
b8175dbf17
commit
b22207b180
@ -5,7 +5,15 @@ import { useRequest } from '../api-client';
|
||||
export const CurrentAppInfoContext = createContext(null);
|
||||
|
||||
export const useCurrentAppInfo = () => {
|
||||
return useContext(CurrentAppInfoContext);
|
||||
return useContext<{
|
||||
data: {
|
||||
database: {
|
||||
dialect: string;
|
||||
};
|
||||
lang: string;
|
||||
version: string;
|
||||
};
|
||||
}>(CurrentAppInfoContext);
|
||||
};
|
||||
export const CurrentAppInfoProvider = (props) => {
|
||||
const result = useRequest({
|
||||
|
@ -9,10 +9,11 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useRequest } from '../../api-client';
|
||||
import { RecordProvider, useRecord } from '../../record-provider';
|
||||
import { ActionContextProvider, SchemaComponent, useActionContext, useCompile } from '../../schema-component';
|
||||
import { useResourceActionContext, useResourceContext } from '../ResourceActionProvider';
|
||||
import { useCancelAction } from '../action-hooks';
|
||||
import { useCollectionManager } from '../hooks';
|
||||
import useDialect from '../hooks/useDialect';
|
||||
import { IField } from '../interfaces/types';
|
||||
import { useResourceActionContext, useResourceContext } from '../ResourceActionProvider';
|
||||
import * as components from './components';
|
||||
import { getOptions } from './interfaces';
|
||||
|
||||
@ -176,6 +177,8 @@ export const AddFieldAction = (props) => {
|
||||
const [schema, setSchema] = useState({});
|
||||
const compile = useCompile();
|
||||
const { t } = useTranslation();
|
||||
const { isDialect } = useDialect();
|
||||
|
||||
const currentCollections = useMemo(() => {
|
||||
return collections.map((v) => {
|
||||
return {
|
||||
@ -298,6 +301,8 @@ export const AddFieldAction = (props) => {
|
||||
showReverseFieldConfig: true,
|
||||
targetScope,
|
||||
collections: currentCollections,
|
||||
isDialect,
|
||||
disabledJSONB: false,
|
||||
...scope,
|
||||
}}
|
||||
/>
|
||||
|
@ -8,10 +8,11 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useAPIClient, useRequest } from '../../api-client';
|
||||
import { RecordProvider, useRecord } from '../../record-provider';
|
||||
import { ActionContextProvider, SchemaComponent, useActionContext, useCompile } from '../../schema-component';
|
||||
import { useResourceActionContext, useResourceContext } from '../ResourceActionProvider';
|
||||
import { useCancelAction, useUpdateAction } from '../action-hooks';
|
||||
import { useCollectionManager } from '../hooks';
|
||||
import useDialect from '../hooks/useDialect';
|
||||
import { IField } from '../interfaces/types';
|
||||
import { useResourceActionContext, useResourceContext } from '../ResourceActionProvider';
|
||||
import * as components from './components';
|
||||
|
||||
const getSchema = (schema: IField, record: any, compile, getContainer): ISchema => {
|
||||
@ -144,6 +145,8 @@ export const EditFieldAction = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const compile = useCompile();
|
||||
const [data, setData] = useState<any>({});
|
||||
const { isDialect } = useDialect();
|
||||
|
||||
const currentCollections = useMemo(() => {
|
||||
return collections.map((v) => {
|
||||
return {
|
||||
@ -194,6 +197,8 @@ export const EditFieldAction = (props) => {
|
||||
useCancelAction,
|
||||
showReverseFieldConfig: !data?.reverseField,
|
||||
collections: currentCollections,
|
||||
isDialect,
|
||||
disabledJSONB: true,
|
||||
...scope,
|
||||
}}
|
||||
/>
|
||||
|
@ -0,0 +1,16 @@
|
||||
import { useCurrentAppInfo } from '../../appInfo';
|
||||
|
||||
const useDialect = () => {
|
||||
const {
|
||||
data: { database },
|
||||
} = useCurrentAppInfo();
|
||||
|
||||
const isDialect = (dialect: string) => database?.dialect === dialect;
|
||||
|
||||
return {
|
||||
isDialect,
|
||||
dialect: database?.dialect,
|
||||
};
|
||||
};
|
||||
|
||||
export default useDialect;
|
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import { render, screen, waitFor } from 'testUtils';
|
||||
import { CurrentAppInfoContext } from '../../../appInfo';
|
||||
import { Checkbox } from '../../../schema-component/antd/checkbox';
|
||||
import { Input } from '../../../schema-component/antd/input';
|
||||
import { SchemaComponent } from '../../../schema-component/core/SchemaComponent';
|
||||
import { SchemaComponentProvider } from '../../../schema-component/core/SchemaComponentProvider';
|
||||
import { json } from '../json';
|
||||
|
||||
const Component = () => {
|
||||
return (
|
||||
<SchemaComponentProvider components={{ Input, Checkbox }}>
|
||||
<SchemaComponent schema={json} />
|
||||
</SchemaComponentProvider>
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: 需要先修复测试中的路径问题:即某些引用路径返回的模块是 undefined
|
||||
describe('JSON', () => {
|
||||
it('should show JSONB when dialect is postgres', async () => {
|
||||
render(<Component />, {
|
||||
wrapper: ({ children }) => (
|
||||
<CurrentAppInfoContext.Provider
|
||||
value={{
|
||||
data: {
|
||||
database: {
|
||||
dialect: 'postgres',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CurrentAppInfoContext.Provider>
|
||||
),
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('JSONB')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
@ -1,4 +1,6 @@
|
||||
import { FormItem, FormLayout } from '@formily/antd-v5';
|
||||
import { registerValidateRules } from '@formily/core';
|
||||
import React from 'react';
|
||||
import { defaultProps } from './properties';
|
||||
import { IField } from './types';
|
||||
|
||||
@ -43,6 +45,19 @@ export const json: IField = {
|
||||
hasDefaultValue: true,
|
||||
properties: {
|
||||
...defaultProps,
|
||||
jsonb: {
|
||||
type: 'boolean',
|
||||
title: 'JSONB',
|
||||
// 不直接用 `FormItem` 的原因是为了想要设置 `FormLayout` 的 `layout` 属性为 `horizontal` (默认就是 horizontal)
|
||||
'x-decorator': ({ children }) => (
|
||||
<FormLayout>
|
||||
<FormItem>{children}</FormItem>
|
||||
</FormLayout>
|
||||
),
|
||||
'x-component': 'Checkbox',
|
||||
'x-hidden': `{{ !isDialect('postgres') }}`,
|
||||
'x-disabled': `{{ disabledJSONB }}`,
|
||||
},
|
||||
},
|
||||
filterable: {},
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
import { observer, RecursionField, useField, useFieldSchema, useForm } from '@formily/react';
|
||||
import { lodash } from '@nocobase/utils';
|
||||
import { lodash } from '@nocobase/utils/client';
|
||||
import { App, Button, Popover } from 'antd';
|
||||
import classnames from 'classnames';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
@ -3,6 +3,11 @@ import { BaseColumnFieldOptions, Field } from './field';
|
||||
|
||||
export class JsonField extends Field {
|
||||
get dataType() {
|
||||
const dialect = this.context.database.sequelize.getDialect();
|
||||
const { jsonb } = this.options;
|
||||
if (dialect === 'postgres' && jsonb) {
|
||||
return DataTypes.JSONB;
|
||||
}
|
||||
return DataTypes.JSON;
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,9 @@ const Entity: React.FC<{
|
||||
},
|
||||
id,
|
||||
} = node;
|
||||
const database = useCurrentAppInfo();
|
||||
const {
|
||||
data: { database },
|
||||
} = useCurrentAppInfo();
|
||||
const collectionData = useRef();
|
||||
const categoryData = useContext(CollectionCategroriesContext);
|
||||
collectionData.current = { ...item, title, inherits: item.inherits && new Proxy(item.inherits, {}) };
|
||||
@ -184,7 +186,9 @@ const PortsCom = React.memo<any>(({ targetGraph, collectionData, setTargetNode,
|
||||
const [collapse, setCollapse] = useState(false);
|
||||
const { t } = useGCMTranslation();
|
||||
const compile = useCompile();
|
||||
const database = useCurrentAppInfo();
|
||||
const {
|
||||
data: { database },
|
||||
} = useCurrentAppInfo();
|
||||
const portsData = lodash.groupBy(ports.items, (v) => {
|
||||
if (
|
||||
v.isForeignKey ||
|
||||
|
@ -20595,6 +20595,11 @@ prettier@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz#e7b19f691245a21d618c68bc54dc06122f6105ae"
|
||||
|
||||
prettier@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmmirror.com/prettier/-/prettier-3.0.0.tgz#e7b19f691245a21d618c68bc54dc06122f6105ae"
|
||||
integrity sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==
|
||||
|
||||
pretty-error@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmmirror.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6"
|
||||
|
Loading…
Reference in New Issue
Block a user