fix: hera/core组件迁移到core (#1453)
Reviewed-on: daoyoucloud/tachybase#1453 Co-authored-by: wjh <wwwjh0710@163.com> Co-committed-by: wjh <wwwjh0710@163.com>
This commit is contained in:
parent
a85bc15b95
commit
9dc412dbf3
@ -949,5 +949,6 @@
|
|||||||
"AssociationCascader":"关系型级联选择",
|
"AssociationCascader":"关系型级联选择",
|
||||||
"Edit Collection":"修改数据",
|
"Edit Collection":"修改数据",
|
||||||
"Field Component":"字段组件",
|
"Field Component":"字段组件",
|
||||||
"Custom filter":"自定义筛选"
|
"Custom filter":"自定义筛选",
|
||||||
|
"Parent Association field":"父级关联字段"
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,136 @@
|
|||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { useCollectionManager, useRequest } from '@tachybase/client';
|
||||||
|
import { connect, mapProps } from '@tachybase/schema';
|
||||||
|
|
||||||
|
import { Cascader } from 'antd';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export const AssociationCascader = connect(
|
||||||
|
(props) => {
|
||||||
|
const { fieldNames, collection, associationField } = props;
|
||||||
|
const cm = useCollectionManager();
|
||||||
|
const titleField = cm.getCollection(collection).titleField;
|
||||||
|
const joinTitleField = cm.getCollection(collection + '.' + associationField).titleField;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
appends: [associationField],
|
||||||
|
fields: [titleField],
|
||||||
|
pageSize: 99999,
|
||||||
|
filter: props?.params?.filter ? { ...props?.params?.filter } : {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = useRequest<any>({
|
||||||
|
resource: collection,
|
||||||
|
action: 'list',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
|
||||||
|
const options = useMemo(() => {
|
||||||
|
if (props.chartCascader) {
|
||||||
|
const dataOptions = {};
|
||||||
|
const options = [];
|
||||||
|
data?.data.forEach((item) => {
|
||||||
|
if (item?.[associationField]) {
|
||||||
|
const field = [];
|
||||||
|
if (Array.isArray(item[associationField]) && item[associationField].length) {
|
||||||
|
field.push(...item[associationField]);
|
||||||
|
} else {
|
||||||
|
field.push({ ...item[associationField] });
|
||||||
|
}
|
||||||
|
field.forEach((fieldItem) => {
|
||||||
|
if (!Object.keys(fieldItem).length) return;
|
||||||
|
if (dataOptions[fieldItem[joinTitleField]]) {
|
||||||
|
dataOptions[fieldItem[joinTitleField]].push({
|
||||||
|
label: item[titleField],
|
||||||
|
value: item[titleField],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dataOptions[fieldItem[joinTitleField]] = [
|
||||||
|
{
|
||||||
|
label: item[titleField],
|
||||||
|
value: item[titleField],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (const key in dataOptions) {
|
||||||
|
options.push({
|
||||||
|
label: key,
|
||||||
|
value: key,
|
||||||
|
children: [...dataOptions[key]],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
} else {
|
||||||
|
const dict: { [key: string]: string[] } =
|
||||||
|
data?.data.reduce((acc, item) => {
|
||||||
|
if (item[associationField]) {
|
||||||
|
const key = item[associationField][joinTitleField];
|
||||||
|
acc[key] ??= [];
|
||||||
|
acc[key].push(item[titleField]);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {}) || {};
|
||||||
|
const options = Object.entries(dict).map(([key, values]) => ({
|
||||||
|
[fieldNames.label]: key,
|
||||||
|
[fieldNames.value]: key,
|
||||||
|
children: values.map((value) => ({ [fieldNames.label]: value, [fieldNames.value]: value })),
|
||||||
|
}));
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}, [associationField, joinTitleField, titleField, data?.data]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SingleValueCascader
|
||||||
|
options={options}
|
||||||
|
{...props}
|
||||||
|
showSearch
|
||||||
|
titleField={titleField}
|
||||||
|
joinTitleField={joinTitleField}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
mapProps((props) => {
|
||||||
|
return { ...props };
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const SingleValueCascader = (props) => {
|
||||||
|
const { value, options, onChange, fieldNames, chartCascader, titleField, joinTitleField } = props;
|
||||||
|
const fieldLabel = fieldNames ? fieldNames.value : titleField;
|
||||||
|
const joinFieldLabel = fieldNames ? fieldNames.value : 'value';
|
||||||
|
const arrayValue = value && options ? [] : undefined;
|
||||||
|
if (arrayValue) {
|
||||||
|
for (const option of options) {
|
||||||
|
if (option[fieldLabel] === value) {
|
||||||
|
arrayValue.push(option[fieldLabel]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (const subOption of option.children) {
|
||||||
|
if (subOption[joinFieldLabel] === value) {
|
||||||
|
arrayValue.push(option[joinFieldLabel]);
|
||||||
|
arrayValue.push(subOption[joinFieldLabel]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const newProps = {
|
||||||
|
...props,
|
||||||
|
value: arrayValue,
|
||||||
|
onChange: (v) => {
|
||||||
|
if (v) {
|
||||||
|
onChange(v[v.length - 1]);
|
||||||
|
} else {
|
||||||
|
onChange(v);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return <Cascader {...newProps} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
SingleValueCascader.displayName = 'SingleValueCascader';
|
||||||
|
AssociationCascader.displayName = 'AssociationCascader';
|
||||||
|
export default AssociationCascader;
|
@ -0,0 +1 @@
|
|||||||
|
export * from './AssociationCascader';
|
@ -1,11 +1,68 @@
|
|||||||
import { connect, mapProps, mapReadPretty } from '@tachybase/schema';
|
import React, { useState } from 'react';
|
||||||
|
import { connect, mapProps, mapReadPretty, useFieldSchema } from '@tachybase/schema';
|
||||||
|
import { fuzzysearch } from '@tachybase/utils/client';
|
||||||
|
|
||||||
|
import { useAsyncEffect } from 'ahooks';
|
||||||
import { AutoComplete as AntdAutoComplete } from 'antd';
|
import { AutoComplete as AntdAutoComplete } from 'antd';
|
||||||
|
|
||||||
|
import { useAPIClient } from '../../../api-client';
|
||||||
import { ReadPretty } from '../input';
|
import { ReadPretty } from '../input';
|
||||||
|
|
||||||
export const AutoComplete = connect(
|
export const AutoComplete = connect(
|
||||||
AntdAutoComplete,
|
(props) => {
|
||||||
|
const { fieldNames, params: fieldFilter } = props;
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const targetKey = fieldNames?.value;
|
||||||
|
const api = useAPIClient();
|
||||||
|
const [defultOptions, setDefultOptions] = useState([]);
|
||||||
|
const [options, setOptions] = useState([]);
|
||||||
|
|
||||||
|
const getNoDuplicateTextOptions = (rawOptions) => {
|
||||||
|
const targetOptions = rawOptions.filter((option, index, self) => {
|
||||||
|
return self.findIndex((item) => item[targetKey] === option[targetKey]) === index;
|
||||||
|
});
|
||||||
|
return targetOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
useAsyncEffect(async () => {
|
||||||
|
const {
|
||||||
|
data: { data: rawOptions },
|
||||||
|
} = await api.request({
|
||||||
|
url: fieldSchema['collectionName'] + ':list',
|
||||||
|
params: {
|
||||||
|
pageSize: 99999,
|
||||||
|
filter: fieldFilter ? { ...fieldFilter.filter } : {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const targetOptions = getNoDuplicateTextOptions(rawOptions);
|
||||||
|
setDefultOptions(targetOptions);
|
||||||
|
setOptions(targetOptions);
|
||||||
|
}, [fieldFilter?.filter, fieldSchema['collectionName']]);
|
||||||
|
|
||||||
|
const onSearch = (value) => {
|
||||||
|
// 在筛选项开头跟踪用户的原始输入值
|
||||||
|
if (value) {
|
||||||
|
setOptions([
|
||||||
|
{
|
||||||
|
[targetKey]: value,
|
||||||
|
},
|
||||||
|
...defultOptions,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
setOptions(defultOptions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AntdAutoComplete
|
||||||
|
{...props}
|
||||||
|
options={options}
|
||||||
|
filterOption={(inputValue, option) => fuzzysearch(inputValue || [], option[targetKey]?.toString() || '')}
|
||||||
|
allowClear
|
||||||
|
onSearch={onSearch}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
mapProps({
|
mapProps({
|
||||||
dataSource: 'options',
|
dataSource: 'options',
|
||||||
}),
|
}),
|
||||||
|
@ -6,6 +6,7 @@ export * from './action';
|
|||||||
export * from './appends-tree-select';
|
export * from './appends-tree-select';
|
||||||
export * from './association-field';
|
export * from './association-field';
|
||||||
export * from './association-select';
|
export * from './association-select';
|
||||||
|
export * from './association-cascader';
|
||||||
export * from './auto-complete';
|
export * from './auto-complete';
|
||||||
export * from './block-item';
|
export * from './block-item';
|
||||||
export * from './card-item';
|
export * from './card-item';
|
||||||
|
@ -226,7 +226,7 @@ export const FilterCustomItemInitializer: React.FC<{
|
|||||||
},
|
},
|
||||||
associationField: {
|
associationField: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: t('Association field'),
|
title: t('Parent Association field'),
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'Select',
|
'x-component': 'Select',
|
||||||
'x-visible': false,
|
'x-visible': false,
|
||||||
|
@ -47,10 +47,6 @@ export const nextTick = (fn: () => void) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function fuzzysearch(needle: string, haystack: string): boolean {
|
export function fuzzysearch(needle: string, haystack: string): boolean {
|
||||||
if (!needle || !haystack) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const hlen = haystack.length;
|
const hlen = haystack.length;
|
||||||
const nlen = needle.length;
|
const nlen = needle.length;
|
||||||
|
|
||||||
|
@ -39,8 +39,6 @@ import {
|
|||||||
import { TstzrangeFieldInterface } from './interfaces/TstzrangeFieldInterface';
|
import { TstzrangeFieldInterface } from './interfaces/TstzrangeFieldInterface';
|
||||||
import { Locale, tval } from './locale';
|
import { Locale, tval } from './locale';
|
||||||
import { AdminLayout, DetailsPage, PageLayout } from './pages';
|
import { AdminLayout, DetailsPage, PageLayout } from './pages';
|
||||||
import { AutoComplete } from './schema-components';
|
|
||||||
import AssociationCascader from './schema-components/association-cascader/AssociationCascader';
|
|
||||||
import { CreateSubmitActionInitializer, SettingBlockInitializer } from './schema-initializer';
|
import { CreateSubmitActionInitializer, SettingBlockInitializer } from './schema-initializer';
|
||||||
import { useCreateActionProps } from './schema-initializer/actions/hooks/useCreateActionProps';
|
import { useCreateActionProps } from './schema-initializer/actions/hooks/useCreateActionProps';
|
||||||
import { EditTitle, IsTablePageSize, PageModeSetting, usePaginationVisible } from './schema-settings';
|
import { EditTitle, IsTablePageSize, PageModeSetting, usePaginationVisible } from './schema-settings';
|
||||||
@ -122,9 +120,7 @@ export class PluginCoreClient extends Plugin {
|
|||||||
|
|
||||||
this.app.addComponents({
|
this.app.addComponents({
|
||||||
AdminLayout,
|
AdminLayout,
|
||||||
AssociationCascader,
|
|
||||||
AssociatedField,
|
AssociatedField,
|
||||||
AutoComplete,
|
|
||||||
CalcResult,
|
CalcResult,
|
||||||
CodeMirror,
|
CodeMirror,
|
||||||
CreateSubmitActionInitializer,
|
CreateSubmitActionInitializer,
|
||||||
|
@ -1,131 +0,0 @@
|
|||||||
import React, { useMemo } from 'react';
|
|
||||||
import { useCollectionManager, useRequest } from '@tachybase/client';
|
|
||||||
import { connect } from '@tachybase/schema';
|
|
||||||
|
|
||||||
import { Cascader } from 'antd';
|
|
||||||
import _ from 'lodash';
|
|
||||||
|
|
||||||
const AssociationCascader = connect((props) => {
|
|
||||||
const { fieldNames, collection, associationField } = props;
|
|
||||||
const cm = useCollectionManager();
|
|
||||||
const titleField = cm.getCollection(collection).titleField;
|
|
||||||
const joinTitleField = cm.getCollection(collection + '.' + associationField).titleField;
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
appends: [associationField],
|
|
||||||
fields: [titleField],
|
|
||||||
pageSize: 99999,
|
|
||||||
filter: props?.params?.filter ? { ...props?.params?.filter } : {},
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data } = useRequest<any>({
|
|
||||||
resource: collection,
|
|
||||||
action: 'list',
|
|
||||||
params,
|
|
||||||
});
|
|
||||||
|
|
||||||
const options = useMemo(() => {
|
|
||||||
if (props.chartCascader) {
|
|
||||||
const dataOptions = {};
|
|
||||||
const options = [];
|
|
||||||
data?.data.forEach((item) => {
|
|
||||||
if (item?.[associationField]) {
|
|
||||||
const field = [];
|
|
||||||
if (Array.isArray(item[associationField]) && item[associationField].length) {
|
|
||||||
field.push(...item[associationField]);
|
|
||||||
} else {
|
|
||||||
field.push({ ...item[associationField] });
|
|
||||||
}
|
|
||||||
field.forEach((fieldItem) => {
|
|
||||||
if (!Object.keys(fieldItem).length) return;
|
|
||||||
if (dataOptions[fieldItem[joinTitleField]]) {
|
|
||||||
dataOptions[fieldItem[joinTitleField]].push({
|
|
||||||
label: item[titleField],
|
|
||||||
value: item[titleField],
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
dataOptions[fieldItem[joinTitleField]] = [
|
|
||||||
{
|
|
||||||
label: item[titleField],
|
|
||||||
value: item[titleField],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
for (const key in dataOptions) {
|
|
||||||
options.push({
|
|
||||||
label: key,
|
|
||||||
value: key,
|
|
||||||
children: [...dataOptions[key]],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return options;
|
|
||||||
} else {
|
|
||||||
const dict: { [key: string]: string[] } =
|
|
||||||
data?.data.reduce((acc, item) => {
|
|
||||||
if (item[associationField]) {
|
|
||||||
const key = item[associationField][joinTitleField];
|
|
||||||
acc[key] ??= [];
|
|
||||||
acc[key].push(item[titleField]);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, {}) || {};
|
|
||||||
const options = Object.entries(dict).map(([key, values]) => ({
|
|
||||||
[fieldNames.label]: key,
|
|
||||||
[fieldNames.value]: key,
|
|
||||||
children: values.map((value) => ({ [fieldNames.label]: value, [fieldNames.value]: value })),
|
|
||||||
}));
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
}, [associationField, joinTitleField, titleField, data?.data]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SingleValueCascader
|
|
||||||
options={options}
|
|
||||||
{...props}
|
|
||||||
showSearch
|
|
||||||
titleField={titleField}
|
|
||||||
joinTitleField={joinTitleField}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const SingleValueCascader = (props) => {
|
|
||||||
const { value, options, onChange, fieldNames, chartCascader, titleField, joinTitleField } = props;
|
|
||||||
const fieldLabel = fieldNames ? fieldNames.value : titleField;
|
|
||||||
const joinFieldLabel = fieldNames ? fieldNames.value : 'value';
|
|
||||||
const arrayValue = value && options ? [] : undefined;
|
|
||||||
if (arrayValue) {
|
|
||||||
for (const option of options) {
|
|
||||||
if (option[fieldLabel] === value) {
|
|
||||||
arrayValue.push(option[fieldLabel]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (const subOption of option.children) {
|
|
||||||
if (subOption[joinFieldLabel] === value) {
|
|
||||||
arrayValue.push(option[joinFieldLabel]);
|
|
||||||
arrayValue.push(subOption[joinFieldLabel]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const newProps = {
|
|
||||||
...props,
|
|
||||||
value: arrayValue,
|
|
||||||
onChange: (v) => {
|
|
||||||
if (v) {
|
|
||||||
onChange(v[v.length - 1]);
|
|
||||||
} else {
|
|
||||||
onChange(v);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return <Cascader {...newProps} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
SingleValueCascader.displayName = 'SingleValueCascader';
|
|
||||||
AssociationCascader.displayName = 'AssociationCascader';
|
|
||||||
export default AssociationCascader;
|
|
@ -1,65 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { useAPIClient } from '@tachybase/client';
|
|
||||||
import { connect, useFieldSchema } from '@tachybase/schema';
|
|
||||||
|
|
||||||
import { useAsyncEffect } from 'ahooks';
|
|
||||||
import { AutoComplete as AntdAutoComplete } from 'antd';
|
|
||||||
|
|
||||||
import { fuzzysearch } from '../../utils';
|
|
||||||
|
|
||||||
export const AutoComplete = connect((props) => {
|
|
||||||
const { fieldNames, params: fieldFilter } = props;
|
|
||||||
const fieldSchema = useFieldSchema();
|
|
||||||
const targetKey = fieldNames?.value;
|
|
||||||
const api = useAPIClient();
|
|
||||||
const [defultOptions, setDefultOptions] = useState([]);
|
|
||||||
const [options, setOptions] = useState([]);
|
|
||||||
|
|
||||||
const getNoDuplicateTextOptions = (rawOptions) => {
|
|
||||||
const targetOptions = rawOptions.filter((option, index, self) => {
|
|
||||||
return self.findIndex((item) => item[targetKey] === option[targetKey]) === index;
|
|
||||||
});
|
|
||||||
return targetOptions;
|
|
||||||
};
|
|
||||||
|
|
||||||
useAsyncEffect(async () => {
|
|
||||||
const {
|
|
||||||
data: { data: rawOptions },
|
|
||||||
} = await api.request({
|
|
||||||
url: fieldSchema['collectionName'] + ':list',
|
|
||||||
params: {
|
|
||||||
pageSize: 99999,
|
|
||||||
filter: fieldFilter ? { ...fieldFilter.filter } : {},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const targetOptions = getNoDuplicateTextOptions(rawOptions);
|
|
||||||
setDefultOptions(targetOptions);
|
|
||||||
setOptions(targetOptions);
|
|
||||||
}, [fieldFilter?.filter, fieldSchema['collectionName']]);
|
|
||||||
|
|
||||||
const onSearch = (value) => {
|
|
||||||
// 在筛选项开头跟踪用户的原始输入值
|
|
||||||
if (value) {
|
|
||||||
setOptions([
|
|
||||||
{
|
|
||||||
[targetKey]: value,
|
|
||||||
},
|
|
||||||
...defultOptions,
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
setOptions(defultOptions);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AntdAutoComplete
|
|
||||||
{...props}
|
|
||||||
options={options}
|
|
||||||
filterOption={(inputValue, option) => fuzzysearch(inputValue, option[targetKey]?.toString())}
|
|
||||||
allowClear
|
|
||||||
onSearch={onSearch}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
AutoComplete.displayName = 'AutoComplete';
|
|
||||||
export default AutoComplete;
|
|
@ -1 +0,0 @@
|
|||||||
export * from './auto-complete/AutoComplete';
|
|
Loading…
Reference in New Issue
Block a user