refactor(client): add component to support data source select (#3691)
This commit is contained in:
parent
d904d13c52
commit
f68b2fb04a
@ -132,10 +132,10 @@ export const useSelfAndChildrenCollections = (collectionName: string) => {
|
|||||||
return options;
|
return options;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useCollectionFilterOptions = (collection: any) => {
|
export const useCollectionFilterOptions = (collection: any, dataSource?: string) => {
|
||||||
const { getCollectionFields, getInterface } = useCollectionManager_deprecated();
|
const { getCollectionFields, getInterface } = useCollectionManager_deprecated();
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const fields = getCollectionFields(collection);
|
const fields = getCollectionFields(collection, dataSource);
|
||||||
const field2option = (field, depth) => {
|
const field2option = (field, depth) => {
|
||||||
if (!field.interface) {
|
if (!field.interface) {
|
||||||
return;
|
return;
|
||||||
@ -165,7 +165,7 @@ export const useCollectionFilterOptions = (collection: any) => {
|
|||||||
option['children'] = children;
|
option['children'] = children;
|
||||||
}
|
}
|
||||||
if (nested) {
|
if (nested) {
|
||||||
const targetFields = getCollectionFields(field.target);
|
const targetFields = getCollectionFields(field.target, dataSource);
|
||||||
const options = getOptions(targetFields, depth + 1).filter(Boolean);
|
const options = getOptions(targetFields, depth + 1).filter(Boolean);
|
||||||
option['children'] = option['children'] || [];
|
option['children'] = option['children'] || [];
|
||||||
option['children'].push(...options);
|
option['children'].push(...options);
|
||||||
@ -184,7 +184,7 @@ export const useCollectionFilterOptions = (collection: any) => {
|
|||||||
};
|
};
|
||||||
const options = getOptions(fields, 1);
|
const options = getOptions(fields, 1);
|
||||||
return options;
|
return options;
|
||||||
}, [_.isString(collection) ? collection : collection?.name]);
|
}, [_.isString(collection) ? collection : collection?.name, dataSource]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useCollectionFilterOptionsV2 = (collection: any) => {
|
export const useCollectionFilterOptionsV2 = (collection: any) => {
|
||||||
|
@ -25,3 +25,4 @@ export * from './collectionPlugin';
|
|||||||
export * from './mixins/InheritanceCollectionMixin';
|
export * from './mixins/InheritanceCollectionMixin';
|
||||||
export * from './sub-table';
|
export * from './sub-table';
|
||||||
export * from './CollectionHistoryProvider';
|
export * from './CollectionHistoryProvider';
|
||||||
|
export * from './utils';
|
||||||
|
16
packages/core/client/src/collection-manager/utils.ts
Normal file
16
packages/core/client/src/collection-manager/utils.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export function parseCollectionName(collection: string) {
|
||||||
|
if (!collection) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const dataSourceCollection = collection.split(':');
|
||||||
|
const collectionName = dataSourceCollection.pop();
|
||||||
|
const dataSourceName = dataSourceCollection[0] ?? 'main';
|
||||||
|
return [dataSourceName, collectionName];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function joinCollectionName(dataSourceName: string, collectionName: string) {
|
||||||
|
if (!dataSourceName || dataSourceName === 'main') {
|
||||||
|
return collectionName;
|
||||||
|
}
|
||||||
|
return `${dataSourceName}:${collectionName}`;
|
||||||
|
}
|
@ -851,7 +851,7 @@
|
|||||||
"loading": "加载中",
|
"loading": "加载中",
|
||||||
"name is required": "名称不能为空",
|
"name is required": "名称不能为空",
|
||||||
"The {{type}} \"{{name}}\" may have been deleted. Please remove this {{blockType}}.": "{{type}} \"{{name}}\" 可能已被删除。请删除当前{{blockType}}.",
|
"The {{type}} \"{{name}}\" may have been deleted. Please remove this {{blockType}}.": "{{type}} \"{{name}}\" 可能已被删除。请删除当前{{blockType}}.",
|
||||||
"data source": "数据源",
|
"Data source": "数据源",
|
||||||
"DataSource": "数据源",
|
"DataSource": "数据源",
|
||||||
"Data model": "数据模型",
|
"Data model": "数据模型",
|
||||||
"Security": "认证与安全",
|
"Security": "认证与安全",
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
import { connect, mapReadPretty, observer, useField } from '@formily/react';
|
import { connect, mapReadPretty, observer, useField } from '@formily/react';
|
||||||
import { Select, SelectProps, Tag } from 'antd';
|
import { Cascader, Select, SelectProps, Tag } from 'antd';
|
||||||
import React, { useContext } from 'react';
|
import React, { useCallback, useContext, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSelfAndChildrenCollections } from '../../../collection-manager/action-hooks';
|
import { useSelfAndChildrenCollections } from '../../../collection-manager/action-hooks';
|
||||||
import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager/hooks';
|
import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager/hooks';
|
||||||
import { useCompile } from '../../hooks';
|
import { useCompile } from '../../hooks';
|
||||||
import { FilterContext } from '../filter/context';
|
import { FilterContext } from '../filter/context';
|
||||||
|
import { useDataSourceManager } from '../../../data-source';
|
||||||
|
import { parseCollectionName } from '../../../collection-manager';
|
||||||
|
|
||||||
export type CollectionSelectProps = SelectProps<any, any> & {
|
export type CollectionSelectProps = SelectProps<any, any> & {
|
||||||
filter?: (item: any, index: number, array: any[]) => boolean;
|
filter?: (item: any, index: number, array: any[]) => boolean;
|
||||||
isTableOid?: boolean;
|
isTableOid?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
function useOptions({ filter, isTableOid }: CollectionSelectProps) {
|
function useCollectionOptions({ filter, isTableOid }: CollectionSelectProps) {
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const field: any = useField();
|
const field: any = useField();
|
||||||
const ctx: any = useContext(FilterContext);
|
const ctx: any = useContext(FilterContext);
|
||||||
@ -29,20 +31,30 @@ function useOptions({ filter, isTableOid }: CollectionSelectProps) {
|
|||||||
typeof filter === 'function'
|
typeof filter === 'function'
|
||||||
? ((inheritCollections || currentCollections) as any[]).filter(filter)
|
? ((inheritCollections || currentCollections) as any[]).filter(filter)
|
||||||
: inheritCollections || currentCollections;
|
: inheritCollections || currentCollections;
|
||||||
return filtered
|
return useMemo(
|
||||||
.filter((item) => !item.hidden)
|
() =>
|
||||||
.map((item) => ({
|
filtered
|
||||||
label: compile(item.title || item.label),
|
.filter((item) => !item.hidden)
|
||||||
value: item.name || item.value,
|
.map((item) => ({
|
||||||
color: item.category?.color,
|
label: compile(item.title || item.label),
|
||||||
}));
|
value: item.name || item.value,
|
||||||
|
color: item.category?.color,
|
||||||
|
})),
|
||||||
|
[filtered],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CollectionSelect = connect(
|
export const CollectionSelect = connect(
|
||||||
(props: CollectionSelectProps) => {
|
(props: CollectionSelectProps) => {
|
||||||
const { filter, ...others } = props;
|
const { filter, ...others } = props;
|
||||||
const options = useOptions(props);
|
const options = useCollectionOptions(props);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const optionFilter = useCallback(
|
||||||
|
(input, option) =>
|
||||||
|
(option?.label.toLowerCase() ?? '').includes(input.toLocaleLowerCase()) ||
|
||||||
|
(option?.value.toString().toLowerCase() ?? '').includes(input.toLocaleLowerCase()),
|
||||||
|
[],
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -52,10 +64,7 @@ export const CollectionSelect = connect(
|
|||||||
popupMatchSelectWidth={false}
|
popupMatchSelectWidth={false}
|
||||||
{...others}
|
{...others}
|
||||||
showSearch
|
showSearch
|
||||||
filterOption={(input, option) =>
|
filterOption={optionFilter}
|
||||||
(option?.label.toLowerCase() ?? '').includes(input.toLocaleLowerCase()) ||
|
|
||||||
(option?.value.toString().toLowerCase() ?? '').includes(input.toLocaleLowerCase())
|
|
||||||
}
|
|
||||||
options={options}
|
options={options}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -65,7 +74,7 @@ export const CollectionSelect = connect(
|
|||||||
(props: CollectionSelectProps) => {
|
(props: CollectionSelectProps) => {
|
||||||
const { mode } = props;
|
const { mode } = props;
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const options = useOptions(props).filter((option) => {
|
const options = useCollectionOptions(props).filter((option) => {
|
||||||
if (mode === 'multiple') {
|
if (mode === 'multiple') {
|
||||||
return (props.value ?? []).includes(option.value);
|
return (props.value ?? []).includes(option.value);
|
||||||
}
|
}
|
||||||
@ -86,3 +95,88 @@ export const CollectionSelect = connect(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export type DataSourceSelectProps = SelectProps<any, any> & {
|
||||||
|
filter?: (item: any, index: number, array: any[]) => boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function useDataSourceOptions({ filter }: DataSourceSelectProps) {
|
||||||
|
const compile = useCompile();
|
||||||
|
const dataSourceManager = useDataSourceManager();
|
||||||
|
const dataSources = dataSourceManager.getDataSources();
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
label: compile('Main'),
|
||||||
|
value: 'main',
|
||||||
|
},
|
||||||
|
...(typeof filter === 'function' ? dataSources.filter(filter) : dataSources).map((item) => ({
|
||||||
|
label: item.displayName,
|
||||||
|
value: item.key,
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
[dataSources, filter],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DataSourceSelect = connect((props: DataSourceSelectProps) => {
|
||||||
|
const { filter, ...others } = props;
|
||||||
|
const options = useDataSourceOptions(props);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const optionFilter = useCallback(
|
||||||
|
(input, option) =>
|
||||||
|
(option?.label.toLowerCase() ?? '').includes(input.toLocaleLowerCase()) ||
|
||||||
|
(option?.value.toString().toLowerCase() ?? '').includes(input.toLocaleLowerCase()),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
// @ts-ignore
|
||||||
|
role="button"
|
||||||
|
data-testid="select-datasource"
|
||||||
|
placeholder={t('Select data source')}
|
||||||
|
popupMatchSelectWidth={false}
|
||||||
|
{...others}
|
||||||
|
showSearch
|
||||||
|
filterOption={optionFilter}
|
||||||
|
options={options}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DataSourceCollectionCascader = connect((props) => {
|
||||||
|
const dataSourceManager = useDataSourceManager();
|
||||||
|
const compile = useCompile();
|
||||||
|
const { value, onChange, dataSourceFilter, collectionFilter, ...others } = props;
|
||||||
|
const [dataSourceName, collectionName] = parseCollectionName(value);
|
||||||
|
const path = [dataSourceName, collectionName].filter(Boolean);
|
||||||
|
const dataSources = dataSourceManager.getDataSources();
|
||||||
|
|
||||||
|
const options = useMemo(() => {
|
||||||
|
return (dataSourceFilter ? dataSources.filter(dataSourceFilter) : dataSources).map((dataSource) => {
|
||||||
|
return {
|
||||||
|
label: compile(dataSource.displayName),
|
||||||
|
value: dataSource.key,
|
||||||
|
children: dataSource.collectionManager.collectionInstancesArr
|
||||||
|
.filter(collectionFilter ?? ((collection) => !collection.hidden))
|
||||||
|
.map((collection) => {
|
||||||
|
return {
|
||||||
|
label: compile(collection.title),
|
||||||
|
value: collection.name,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [dataSources, dataSourceFilter, collectionFilter]);
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(value) => {
|
||||||
|
if (!value) {
|
||||||
|
return onChange(value);
|
||||||
|
}
|
||||||
|
onChange(value[0] === 'main' ? value[1] : value.join(':'));
|
||||||
|
},
|
||||||
|
[onChange],
|
||||||
|
);
|
||||||
|
return <Cascader showSearch {...others} options={options} value={path} onChange={handleChange} />;
|
||||||
|
});
|
||||||
|
@ -9,3 +9,4 @@ export * from './load-default-actions';
|
|||||||
export * from './types';
|
export * from './types';
|
||||||
|
|
||||||
export * from './data-source-with-database';
|
export * from './data-source-with-database';
|
||||||
|
export * from './utils';
|
||||||
|
9
packages/core/data-source-manager/src/utils.ts
Normal file
9
packages/core/data-source-manager/src/utils.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export function parseCollectionName(collection: string) {
|
||||||
|
if (!collection) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const dataSourceCollection = collection.split(':');
|
||||||
|
const collectionName = dataSourceCollection.pop();
|
||||||
|
const dataSourceName = dataSourceCollection[0] ?? 'main';
|
||||||
|
return [dataSourceName, collectionName];
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user