fix: 完善mobile级联组件的地区功能和只读样式 (#1226)

Reviewed-on: daoyoucloud/tachybase#1226
Reviewed-by: sealday <zhanglin@daoyoucloud.com>
Co-authored-by: wjh <wwwjh0710@163.com>
Co-committed-by: wjh <wwwjh0710@163.com>
This commit is contained in:
wjh 2024-06-21 18:12:15 +08:00 committed by sealday
parent 4fddd1023a
commit 299c01da9f
4 changed files with 121 additions and 103 deletions

View File

@ -11,9 +11,7 @@ export const useChinaRegionDataSource = (options) => {
params: { params: {
sort: 'code', sort: 'code',
paginate: false, paginate: false,
filter: { filter: { level: 1 },
level: 1,
},
}, },
}, },
{ {
@ -38,7 +36,7 @@ export const useChinaRegionLoadData = () => {
const api = useAPIClient(); const api = useAPIClient();
const field = useField<ArrayField>(); const field = useField<ArrayField>();
const maxLevel = field.componentProps.maxLevel; const maxLevel = field.componentProps.maxLevel;
return (selectedOptions) => { return (selectedOptions, onSuccess?) => {
const targetOption = selectedOptions[selectedOptions.length - 1]; const targetOption = selectedOptions[selectedOptions.length - 1];
if (targetOption?.children?.length > 0) { if (targetOption?.children?.length > 0) {
return; return;
@ -54,14 +52,18 @@ export const useChinaRegionLoadData = () => {
}, },
}) })
.then(({ data }) => { .then(({ data }) => {
targetOption.loading = false; if (onSuccess) {
targetOption.children = onSuccess(data);
data?.data?.map((item) => { } else {
if (maxLevel > item.level) { targetOption.loading = false;
item.isLeaf = false; targetOption.children =
} data?.data?.map((item) => {
return item; if (maxLevel > item.level) {
}) || []; item.isLeaf = false;
}
return item;
}) || [];
}
field.dataSource = [...field.dataSource]; field.dataSource = [...field.dataSource];
}) })
.catch((err) => { .catch((err) => {

View File

@ -6,7 +6,7 @@ import {
useFieldServiceFilter, useFieldServiceFilter,
useRequest, useRequest,
} from '@tachybase/client'; } from '@tachybase/client';
import { observer, useFieldSchema, useForm } from '@tachybase/schema'; import { ArrayField, observer, useField, useFieldSchema, useForm } from '@tachybase/schema';
import { isArray } from '@tachybase/utils/client'; import { isArray } from '@tachybase/utils/client';
import { Button, CascaderView, Divider, Popup, SearchBar, Space } from 'antd-mobile'; import { Button, CascaderView, Divider, Popup, SearchBar, Space } from 'antd-mobile';
@ -18,7 +18,18 @@ import { MInput } from '../Input';
import { useStyles } from './style'; import { useStyles } from './style';
export const AntdCascader = observer((props) => { export const AntdCascader = observer((props) => {
const { service, fieldNames, disabled, multiple, onChange, value, formCascaderValues, index } = props as any; const {
service,
fieldNames,
disabled,
useLoadData,
useDataSource,
multiple,
onChange,
value,
formCascaderValues,
index,
} = props as any;
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const cm = useCollectionManager(); const cm = useCollectionManager();
const { styles } = useStyles(); const { styles } = useStyles();
@ -34,7 +45,8 @@ export const AntdCascader = observer((props) => {
const [cascaderValue, setCascaderValue] = useState(value); const [cascaderValue, setCascaderValue] = useState(value);
const [valueText, setValueText] = useState(''); const [valueText, setValueText] = useState('');
const [flatOption, setFlatOption] = useState([]); const [flatOption, setFlatOption] = useState([]);
const { run } = useRequest( const field = useField<ArrayField>();
const { loading: collectionLoading, run: collectionRun } = useRequest(
{ {
resource: collection?.name, resource: collection?.name,
action: 'list', action: 'list',
@ -47,7 +59,8 @@ export const AntdCascader = observer((props) => {
manual: true, manual: true,
onSuccess({ data }) { onSuccess({ data }) {
if (data) { if (data) {
setOptions(cascadeOption(data)); setOptions(data);
field.dataSource = data || [];
const valueOption = flatOptions(data); const valueOption = flatOptions(data);
setFlatOption(valueOption); setFlatOption(valueOption);
if (value) { if (value) {
@ -57,39 +70,40 @@ export const AntdCascader = observer((props) => {
}, },
}, },
); );
const loadData = useLoadData?.(props);
const cascadeOption = (option) => { const { loading: areaLoading, run: areaRun } = useDataSource?.({
option.forEach((item) => { onSuccess(data) {
item['value'] = item[fieldNames.value]; field.dataSource = data?.data || [];
item['label'] = item[fieldNames.label]; setOptions(data?.data);
if (item.children) { },
item.children = cascadeOption(item.children); }) || { loading: true, run: null };
} if (!valueText && value && isChinaRegion) {
}); setValueText(getChinaRegionText(value, fieldNames));
return option; }
};
useEffect(() => { useEffect(() => {
if (!isChinaRegion && collection?.name) { if (!isChinaRegion && collection?.name) {
checkedPopup(); checkedPopup();
} }
}, [filter]); }, [filter]);
useEffect(() => { useEffect(() => {
if (options.length) { if (!isChinaRegion && flatOption.length) {
cascadeOption(options); setValueText(getValueText(flatOption, value, '', fieldNames['label']));
} }
}, [fieldSchema['x-component-props']?.['fieldNames']]); }, [fieldSchema['x-component-props']?.['fieldNames']]);
const handleSelect = async (option) => { const handleSelect = async (option) => {
if (option) { if (option) {
setCascaderValue(option[option.length - 1]); setCascaderValue(isChinaRegion ? option : option[option.length - 1]);
} else { } else {
setCascaderValue({}); setCascaderValue({});
} }
}; };
const checkedPopup = () => { const checkedPopup = () => {
if (collection && collection.name) { if (!isChinaRegion) {
run(); collectionRun();
} else {
areaRun();
} }
}; };
@ -135,18 +149,47 @@ export const AntdCascader = observer((props) => {
}} }}
> >
<MobileProvider> <MobileProvider>
<SearchBar {!isChinaRegion ? (
placeholder="请输入内容" <>
value={searchValue} <SearchBar
onChange={(value) => { placeholder="请输入内容"
const paramsFilter = { ...filter }; value={searchValue}
paramsFilter[fieldNames['label']] = { $includes: value }; onChange={(value) => {
setFilter(paramsFilter); const paramsFilter = { ...filter };
setSearchValue(value); paramsFilter[fieldNames['label']] = { $includes: value };
setFilter(paramsFilter);
setSearchValue(value);
}}
/>
<Divider />
</>
) : null}
<CascaderView
loading={isChinaRegion ? areaLoading : collectionLoading}
fieldNames={fieldNames}
options={options}
onChange={(value, extend) => {
if (isChinaRegion) {
const maxLevel = field.componentProps.maxLevel;
const targetOption = extend['items'][extend['items'].length - 1];
if (maxLevel > targetOption.level) {
targetOption.children = [];
}
loadData(extend['items'], (data) => {
if (data?.data.length) {
targetOption.children = data.data.map((item) => {
if (maxLevel > item.level) {
item.isLeaf = false;
}
return item;
});
setOptions([...options]);
}
});
}
handleSelect(extend['items']);
}} }}
/> />
<Divider />
<CascaderView options={options} onChange={(value, extend) => handleSelect(extend['items'])} />
<Space justify="evenly" align="center"> <Space justify="evenly" align="center">
<Button <Button
color="primary" color="primary"
@ -163,20 +206,25 @@ export const AntdCascader = observer((props) => {
<Button <Button
color="primary" color="primary"
onClick={() => { onClick={() => {
const changevalue = ['m2m', 'o2m'].includes(collectionField.interface) const changevalue = multiple ? getChangValue(formCascaderValues, cascaderValue, index) : cascaderValue;
? getChangValue(formCascaderValues, cascaderValue, index)
: cascaderValue;
onChange(changevalue); onChange(changevalue);
setValueText(getValueText(flatOption, cascaderValue, '', fieldNames['label'])); const valText = isChinaRegion
? getChinaRegionText(cascaderValue, fieldNames)
: getValueText(flatOption, cascaderValue, '', fieldNames['label']);
setValueText(valText);
setPopupVisible(false); setPopupVisible(false);
const paramsFilter = { ...filter }; if (!isChinaRegion) {
if (paramsFilter[fieldNames['label']]) delete paramsFilter[fieldNames['label']]; const paramsFilter = { ...filter };
setSearchValue(''); delete paramsFilter[fieldNames['label']];
setFilter(paramsFilter); setSearchValue('');
setFilter(paramsFilter);
}
}} }}
disabled={ disabled={
!changOnSelect !changOnSelect
? cascaderValue && cascaderValue !== '{}' && !cascaderValue?.children ? cascaderValue &&
cascaderValue !== '{}' &&
(!cascaderValue?.children || !cascaderValue?.children.length)
? false ? false
: true : true
: cascaderValue && cascaderValue !== '{}' : cascaderValue && cascaderValue !== '{}'
@ -232,16 +280,18 @@ const getChangValue = (formCascaderValues, cascaderValue, index) => {
return value; return value;
}; };
const getChinaRegionText = (value, fieldNames) => {
return value?.reduce((prev, curr, index) => {
return prev + `${curr[fieldNames['label']]}${value.length - 1 === index ? '' : '/'}`;
}, '');
};
export const InternalCascader = observer((props) => { export const InternalCascader = observer((props) => {
const { value, onChange } = props as any; const { value, onChange, multiple } = props as any;
const fieldSchema = useFieldSchema();
const cm = useCollectionManager();
const collectionField = cm.getCollectionField(fieldSchema['x-collection-field']);
const form = useForm();
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<> <>
{isArray(value) ? ( {isArray(value) && multiple ? (
<> <>
{value.map((item, index) => { {value.map((item, index) => {
const filterProps = { const filterProps = {
@ -256,7 +306,7 @@ export const InternalCascader = observer((props) => {
) : ( ) : (
<AntdCascader {...props} /> <AntdCascader {...props} />
)} )}
{['m2m', 'o2m'].includes(collectionField.interface) ? ( {multiple ? (
<> <>
<Button <Button
style={{ style={{
@ -270,6 +320,7 @@ export const InternalCascader = observer((props) => {
}} }}
onClick={() => { onClick={() => {
const pushValue = value ? [...value, {}] : [{}, {}]; const pushValue = value ? [...value, {}] : [{}, {}];
onChange(pushValue); onChange(pushValue);
}} }}
> >

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { AssociationField, useCollectionManager } from '@tachybase/client';
import { connect, mapProps, mapReadPretty, useFieldSchema } from '@tachybase/schema'; import { connect, mapProps, mapReadPretty, useFieldSchema } from '@tachybase/schema';
import { isArray } from '@tachybase/utils/client';
import { InternalCascader } from './AntdCadcader'; import { InternalCascader } from './AntdCadcader';
import { ReadPretty } from './ReadPretty'; import { ReadPretty } from './ReadPretty';
@ -10,5 +10,11 @@ export const MCascader = connect(
mapProps((props) => { mapProps((props) => {
return { ...props }; return { ...props };
}), }),
mapReadPretty(ReadPretty), mapReadPretty((props) => {
const fieldSchema = useFieldSchema();
const cm = useCollectionManager();
const collectionField = cm.getCollectionField(fieldSchema['x-collection-field']);
const isChinaRegion = collectionField.interface === 'chinaRegion';
return isChinaRegion ? <ReadPretty {...props} /> : <AssociationField.ReadPretty {...props} />;
}),
); );

View File

@ -1,5 +1,5 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { css, useCollectionField, useCollectionManager } from '@tachybase/client'; import { AssociationField, css, useCollectionField, useCollectionManager } from '@tachybase/client';
import { connect, isValid, mapProps, mapReadPretty, useField, useFieldSchema } from '@tachybase/schema'; import { connect, isValid, mapProps, mapReadPretty, useField, useFieldSchema } from '@tachybase/schema';
import { isArray } from '@tachybase/utils/client'; import { isArray } from '@tachybase/utils/client';
@ -28,48 +28,7 @@ export const MSelect = connect(
return { ...filterProps }; return { ...filterProps };
}), }),
mapReadPretty((props) => { mapReadPretty((props) => {
const { value, fieldNames } = props; return <AssociationField.ReadPretty {...props} />;
const collectionField = useCollectionField();
const isSlectField = ['multipleSelect', 'select'].includes(collectionField?.interface);
const field = useField<any>();
const dataSource = field?.dataSource || collectionField?.uiSchema.enum || [];
const fieldNamesLabel = fieldNames?.label || 'label';
if (isSlectField) {
const option = [];
if (!isArray(value)) {
if (typeof value === 'object') {
option.push(value);
} else {
option.push(dataSource.find((item) => item.value === value));
}
} else {
option.push(...value);
}
return (
<div>
{option.map((item, index) => (
<Tag key={index} color={getMobileColor(item.color)}>
{item.label}
</Tag>
))}
</div>
);
} else {
let redValue = '';
if (isArray(value)) {
redValue = value.reduce((prev, curr, index) => {
const currContext = value.length - 1 === index ? '' : ', ';
return prev + curr?.[fieldNamesLabel] + currContext;
}, '');
} else {
redValue = value?.[fieldNamesLabel] || '';
}
return (
<div>
<Space>{redValue}</Space>
</div>
);
}
}), }),
); );