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:
wjh 2024-08-12 11:40:24 +08:00 committed by sealday
parent a85bc15b95
commit 9dc412dbf3
11 changed files with 200 additions and 209 deletions

View File

@ -949,5 +949,6 @@
"AssociationCascader":"关系型级联选择", "AssociationCascader":"关系型级联选择",
"Edit Collection":"修改数据", "Edit Collection":"修改数据",
"Field Component":"字段组件", "Field Component":"字段组件",
"Custom filter":"自定义筛选" "Custom filter":"自定义筛选",
"Parent Association field":"父级关联字段"
} }

View File

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

View File

@ -0,0 +1 @@
export * from './AssociationCascader';

View File

@ -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',
}), }),

View File

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

View File

@ -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,

View File

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

View File

@ -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,

View File

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

View File

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

View File

@ -1 +0,0 @@
export * from './auto-complete/AutoComplete';