refactor(client): add component to support data source select (#3691)

This commit is contained in:
Junyi 2024-03-13 10:23:08 +08:00 committed by GitHub
parent d904d13c52
commit f68b2fb04a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 142 additions and 21 deletions

View File

@ -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) => {

View File

@ -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';

View 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}`;
}

View File

@ -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": "认证与安全",

View File

@ -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} />;
});

View File

@ -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';

View 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];
}