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":"关系型级联选择",
|
||||
"Edit Collection":"修改数据",
|
||||
"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 { useAPIClient } from '../../../api-client';
|
||||
import { ReadPretty } from '../input';
|
||||
|
||||
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({
|
||||
dataSource: 'options',
|
||||
}),
|
||||
|
@ -6,6 +6,7 @@ export * from './action';
|
||||
export * from './appends-tree-select';
|
||||
export * from './association-field';
|
||||
export * from './association-select';
|
||||
export * from './association-cascader';
|
||||
export * from './auto-complete';
|
||||
export * from './block-item';
|
||||
export * from './card-item';
|
||||
|
@ -226,7 +226,7 @@ export const FilterCustomItemInitializer: React.FC<{
|
||||
},
|
||||
associationField: {
|
||||
type: 'string',
|
||||
title: t('Association field'),
|
||||
title: t('Parent Association field'),
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-visible': false,
|
||||
|
@ -47,10 +47,6 @@ export const nextTick = (fn: () => void) => {
|
||||
};
|
||||
|
||||
export function fuzzysearch(needle: string, haystack: string): boolean {
|
||||
if (!needle || !haystack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hlen = haystack.length;
|
||||
const nlen = needle.length;
|
||||
|
||||
|
@ -39,8 +39,6 @@ import {
|
||||
import { TstzrangeFieldInterface } from './interfaces/TstzrangeFieldInterface';
|
||||
import { Locale, tval } from './locale';
|
||||
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 { useCreateActionProps } from './schema-initializer/actions/hooks/useCreateActionProps';
|
||||
import { EditTitle, IsTablePageSize, PageModeSetting, usePaginationVisible } from './schema-settings';
|
||||
@ -122,9 +120,7 @@ export class PluginCoreClient extends Plugin {
|
||||
|
||||
this.app.addComponents({
|
||||
AdminLayout,
|
||||
AssociationCascader,
|
||||
AssociatedField,
|
||||
AutoComplete,
|
||||
CalcResult,
|
||||
CodeMirror,
|
||||
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