fix: 修复表单快速添加和弹窗添加功能 (#1141)

Reviewed-on: daoyoucloud/tachybase#1141
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-07 16:12:49 +08:00 committed by sealday
parent c2c0ec7724
commit 9d98941267
6 changed files with 207 additions and 215 deletions

View File

@ -6,7 +6,6 @@ import { Space } from 'antd-mobile';
import { ApprovalReachDataType } from '../../component/ApprovalReachDataType'; import { ApprovalReachDataType } from '../../component/ApprovalReachDataType';
import { ApprovalTemplateType } from '../../component/ApprovalTemplateType'; import { ApprovalTemplateType } from '../../component/ApprovalTemplateType';
import { PendingStatus } from '../../constants';
import { useTodosContext } from '../provider/todosContext'; import { useTodosContext } from '../provider/todosContext';
import { TabApplicantType } from './TabApplicantType'; import { TabApplicantType } from './TabApplicantType';
import { TabApprovalItem } from './TabApprovalItem'; import { TabApprovalItem } from './TabApprovalItem';

View File

@ -1,7 +0,0 @@
import { createContext, useContext } from 'react';
export const AntdPopupContext = createContext({});
export const useAntdPopupContext = () => {
return useContext(AntdPopupContext);
};

View File

@ -1,25 +1,27 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { observer, RecursionField, useField, useFieldSchema, useForm } from '@tachybase/schema'; import { observer, RecursionField, Schema, useField, useFieldSchema, useForm } from '@tachybase/schema';
import { Button, CheckList, Divider, Modal, PickerView, Popup, SearchBar, Space, Tag, Toast } from 'antd-mobile'; import { Button, CheckList, Divider, Modal, PickerView, Popup, SearchBar, Space, Tag, Toast } from 'antd-mobile';
import { MInput } from '../Input';
import './style'; import './style';
import { import {
BlockItem, BlockItem,
CollectionProvider_deprecated, CollectionProvider,
SchemaComponent,
useAPIClient, useAPIClient,
useCollection,
useCollectionField,
useCollectionManager, useCollectionManager,
useDesignable, useDesignable,
useRequest, useRequest,
} from '@tachybase/client'; } from '@tachybase/client';
import { isArray } from '@tachybase/utils/client'; import { isArray } from '@tachybase/utils/client';
import { getMobileColor } from '../../../CustomColor';
import { MobileProvider } from '../../../provider';
import { MInput } from '../Input';
import { CreateRecordAction } from './CreateRecordAction'; import { CreateRecordAction } from './CreateRecordAction';
import { useInsertSchema } from './hook/hooks';
import schema from './schema';
import { useStyles } from './style'; import { useStyles } from './style';
export const AntdSelect = observer((props) => { export const AntdSelect = observer((props) => {
@ -30,13 +32,21 @@ export const AntdSelect = observer((props) => {
const [options, setOptions] = useState([]); const [options, setOptions] = useState([]);
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const cm = useCollectionManager(); const cm = useCollectionManager();
const [visibleValue, setVisibleValue] = useState({}); const collection = useCollection();
const [filter, setFilter] = useState(service?.params?.filter); const [filter, setFilter] = useState(service?.params?.filter);
const api = useAPIClient(); const api = useAPIClient();
const field = useField();
const { insertAfterBegin } = useDesignable();
const insertAddNewer = useInsertSchema('AddNewer', fieldSchema, insertAfterBegin);
const [selectValue, setSelectValue] = useState(value); const [selectValue, setSelectValue] = useState(value);
const fieldNamesLabel = fieldNames?.label || 'label';
const fieldNamesValue = fieldNames?.value || 'value';
let inputValue = '';
if (isArray(value)) {
value.forEach((item, index) => {
inputValue += `${item.label}${value.length - 1 === index ? '' : ','}`;
});
} else if (value && typeof value === 'object') {
inputValue = value[fieldNamesLabel];
}
const { data, run } = useRequest( const { data, run } = useRequest(
{ {
resource: collectionName, resource: collectionName,
@ -57,8 +67,8 @@ export const AntdSelect = observer((props) => {
const dataOption = data['data']?.map((value) => { const dataOption = data['data']?.map((value) => {
return { return {
...value, ...value,
label: value[fieldNames.label], label: value[fieldNamesLabel],
value: value[fieldNames.value], value: value[fieldNamesValue],
}; };
}); });
setOptions(dataOption); setOptions(dataOption);
@ -71,13 +81,12 @@ export const AntdSelect = observer((props) => {
} else { } else {
const field = cm.getCollectionField(fieldSchema['x-collection-field']); const field = cm.getCollectionField(fieldSchema['x-collection-field']);
const data = field.uiSchema?.enum; const data = field.uiSchema?.enum;
setVisibleValue(data.find((item) => item.value === value) || '');
setOptions(data); setOptions(data);
} }
}; };
const addData = () => { const addData = (data) => {
api data[fieldNamesLabel] = api
.request({ .request({
url: collectionName + ':create', url: collectionName + ':create',
method: 'post', method: 'post',
@ -96,7 +105,7 @@ export const AntdSelect = observer((props) => {
}); });
} }
const paramsFilter = { ...filter }; const paramsFilter = { ...filter };
delete paramsFilter[fieldNames.label]; delete paramsFilter[fieldNamesLabel];
setFilter(paramsFilter); setFilter(paramsFilter);
setSearchValue(''); setSearchValue('');
setPopupVisible(false); setPopupVisible(false);
@ -107,131 +116,108 @@ export const AntdSelect = observer((props) => {
}; };
const quickAdd = () => { const quickAdd = () => {
const data = {}; const data = {};
data[fieldNames.label] = searchValue; data[fieldNamesLabel] = searchValue;
addData(); addData(data);
};
const modalAdd = () => {
const collectionField = cm.getCollectionField(fieldSchema['x-collection-field']);
// console.log('🚀 ~ modalAdd ~ collectionField:', collectionField);
const targetCollection = cm.getCollection(collectionField?.target);
// insertAddNewer(schema.AddNewer);
// const props = {
// fieldSchema,
// field,
// targetCollection,
// };
Modal.show({
content: (
<CollectionProvider_deprecated name={targetCollection?.name}>
<RecursionField
onlyRenderProperties
basePath={field.address}
schema={fieldSchema}
filterProperties={(s) => {
return s['x-component'] === 'AssociationField.AddNewer';
}}
/>
</CollectionProvider_deprecated>
),
showCloseButton: true,
});
}; };
return ( return (
<BlockItem> <CollectionProvider name={collectionName || collection.name}>
{!value || typeof value === 'object' ? ( <BlockItem>
<div <div
onClick={() => { onClick={() => {
if (fieldSchema['x-disabled']) return;
setPopupVisible(true); setPopupVisible(true);
checkedPopup(); checkedPopup();
}} }}
style={{ color: '#c5c5c5' }} style={{ color: '#c5c5c5' }}
> >
{isArray(value) && value.length ? ( {value?.length || (value && !isArray(value)) ? (
value.map((item, index) => ( <MInput value={inputValue} disabled={fieldSchema['x-disabled']} />
<Space key={index}>{`${item.label}${value.length - 1 === index ? '' : ','}`}</Space>
))
) : ( ) : (
<Space>{value?.label || '请选择内容'}</Space> <Space>{fieldSchema['x-disabled'] ? '' : '请选择内容'}</Space>
)} )}
</div> </div>
) : ( <Popup visible={popupVisible} className={`${styles['PopupStyle']}`} closeOnMaskClick>
<Tag color={'default'}>{value?.['label']}</Tag> <MobileProvider>
)} <SearchBar
placeholder="请输入内容"
value={searchValue}
onChange={(value) => {
const paramsFilter = { ...filter };
paramsFilter[fieldNamesLabel] = { $includes: value };
setFilter(paramsFilter);
setSearchValue(value);
}}
/>
{searchValue && addMode === 'quickAdd' ? (
<Space justify="center" align="center" onClick={quickAdd}>
+ {searchValue}
</Space>
) : null}
<Divider />
{multiple || mode === 'multiple' ? (
<CheckList
multiple
className={`${styles['checkListStyle']}`}
onChange={(value) => {
if (!value.length) {
setSelectValue(null);
return;
}
const filterValue = options.filter((item) => value.includes(item.value));
setSelectValue(filterValue);
}}
>
{options.map((item, index) => {
return (
<CheckList.Item key={index} value={item.value}>
{item.label}
</CheckList.Item>
);
})}
</CheckList>
) : (
<PickerView
columns={[options]}
onChange={(value) => {
const changeValue = options.find((item) => item['value'] === value[0]);
setSelectValue(changeValue);
}}
/>
)}
<Popup visible={popupVisible} className={`${styles['PopupStyle']}`} closeOnMaskClick> <Space justify="evenly" align="center">
<SearchBar {addMode === 'modalAdd' ? (
placeholder="请输入内容" <SchemaComponent
value={searchValue} components={{ CreateRecordAction }}
onChange={(value) => { schema={{
const paramsFilter = { ...filter }; type: 'void',
paramsFilter[fieldNames.label] = { $includes: value }; name: 'CreateRecord',
setFilter(paramsFilter); 'x-component': 'CreateRecordAction',
setSearchValue(value); 'x-component-props': { fieldSchema },
}} }}
/> />
{searchValue && addMode === 'quickAdd' ? ( ) : null}
<Space justify="center" align="center" onClick={quickAdd}> <Button
+ {searchValue} color="primary"
</Space> onClick={() => {
) : null} setPopupVisible(false);
<Divider /> }}
{multiple || mode === 'multiple' ? ( >
<CheckList
multiple </Button>
className={`${styles['checkListStyle']}`} <Button
onChange={(value) => { color="primary"
if (!value.length) { onClick={() => {
setSelectValue(null); onChange(selectValue);
return; setPopupVisible(false);
} }}
const filterValue = options.filter((item) => value.includes(item.value)); >
setSelectValue(filterValue);
}} </Button>
> </Space>
{options.map((item, index) => { </MobileProvider>
return ( </Popup>
<CheckList.Item key={index} value={item.value}> </BlockItem>
{item.label} </CollectionProvider>
</CheckList.Item>
);
})}
</CheckList>
) : (
<PickerView
columns={[options]}
onChange={(value) => {
const changeValue = options.find((item) => item['value'] === value[0]);
setSelectValue(changeValue);
}}
/>
)}
<Space justify="evenly" align="center">
{/* {addMode === 'modalAdd' ? (
<Button color="primary" fill="outline" onClick={modalAdd}>
</Button>
) : null} */}
<Button
color="primary"
onClick={() => {
setPopupVisible(false);
}}
>
</Button>
<Button
color="primary"
onClick={() => {
onChange(selectValue);
setPopupVisible(false);
}}
>
</Button>
</Space>
</Popup>
</BlockItem>
); );
}); });

View File

@ -1,29 +1,55 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { import { css, SchemaComponentOptions, useApp, useCollection } from '@tachybase/client';
CollectionProvider, import { observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
CollectionProvider_deprecated,
useActionContext,
useCollectionManager,
useCollectionManager_deprecated,
} from '@tachybase/client';
import { RecursionField } from '@tachybase/schema';
import { useInsertSchema } from './hook/hooks'; import { Button, CenterPopup, Modal, Popup } from 'antd-mobile';
import schema from './schema';
export const CreateRecordAction = ({ field, fieldSchema, targetCollection }) => { import { MobileProvider } from '../../../provider';
const [currentCollection, setCurrentCollection] = useState(targetCollection?.name);
const [currentDataSource, setCurrentDataSource] = useState(targetCollection?.dataSource); export const CreateRecordAction = observer(
return ( (props) => {
<CollectionProvider_deprecated name={currentCollection} dataSource={currentDataSource}> const { fieldSchema } = props as any;
<RecursionField
onlyRenderProperties const [visible, setVisible] = useState(false);
basePath={field.address} const addNew = fieldSchema.reduceProperties((buf, schema) => {
schema={fieldSchema} const found = schema.reduceProperties((buf, schema) => {
filterProperties={(s) => { if (schema['x-component'] === 'AssociationField.AddNewer') {
return s['x-component'] === 'AssociationField.AddNewer'; return schema;
}} }
/> return buf;
</CollectionProvider_deprecated> });
); if (found) {
}; return found;
}
return buf;
}, {});
const modalAdd = () => {
setVisible(true);
};
return (
<div>
<Button color="primary" fill="outline" onClick={modalAdd}>
</Button>
<Popup
visible={visible}
onMaskClick={() => {
setVisible(false);
}}
className={css`
.adm-popup-body {
height: 80vh;
overflow: auto;
}
`}
>
<MobileProvider>
<RecursionField schema={addNew} onlyRenderProperties />
</MobileProvider>
</Popup>
</div>
);
},
{ displayName: 'CreateRecordAction' },
);

View File

@ -15,48 +15,59 @@ export const MSelect = connect(
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const cm = useCollectionManager(); const cm = useCollectionManager();
const collection = cm.getCollection(fieldSchema['x-collection-field']); const collection = cm.getCollection(fieldSchema['x-collection-field']);
const field = useField<any>();
const collectionField = useCollectionField();
const dataSource = field.dataSource || collectionField?.uiSchema.enum || [];
if (collection) { if (collection) {
filterProps['collectionName'] = collection.name; filterProps['collectionName'] = collection.name;
} }
if (typeof filterProps['value'] !== 'object') {
filterProps['value'] = dataSource.filter((item) => item.value.toString() === filterProps['value']);
}
return { ...filterProps }; return { ...filterProps };
}), }),
mapReadPretty((props) => { mapReadPretty((props) => {
const { value, fieldNames } = props; const { value, fieldNames } = props;
const isCollectionField = typeof value === 'object' && fieldNames; const collectionField = useCollectionField();
if (isCollectionField) { const isSlectField = ['multipleSelect', 'select'].includes(collectionField.interface);
const isArrayField = isArray(value); const field = useField<any>();
return isArrayField ? ( const dataSource = field.dataSource || collectionField?.uiSchema.enum || [];
<div> const fieldNamesLabel = fieldNames?.label || 'label';
{value.map((item, index) => ( if (isSlectField) {
<Space key={index}>{`${item?.[fieldNames.label] || ''}${value.length - 1 === index ? '' : ','}`}</Space> const option = [];
))} if (!isArray(value)) {
</div> if (typeof value === 'object') {
) : ( option.push(value);
<Space>{value?.[fieldNames.label] || ''}</Space> } else {
); option.push(dataSource.find((item) => item.value === value));
} else { }
const field = useField<any>(); } else {
const collectionField = useCollectionField(); option.push(...value);
const dataSource = field.dataSource || collectionField?.uiSchema.enum || []; }
const options =
typeof value === 'object'
? value
.map((item) => {
if (dataSource.find((dataItem) => dataItem.value === item.value)) {
return item;
}
})
.filter(Boolean)
: dataSource.filter((item) => item.value.toString() === value);
return ( return (
<div> <div>
{options.map((option, key) => ( {option.map((item, index) => (
<Tag key={key} color={getMobileColor(option.color)} style={{ margin: '5px' }}> <Tag key={index} color={getMobileColor(item.color)}>
{option.label} {item.label}
</Tag> </Tag>
))} ))}
</div> </div>
); );
} else {
let redValue = '';
if (isArray(value)) {
redValue = value.reduce((prev, curr) => {
return prev + curr[fieldNamesLabel];
}, '');
} else {
redValue = value[fieldNamesLabel];
}
return (
<div>
<Space>{redValue}</Space>
</div>
);
} }
}), }),
); );

View File

@ -1,23 +0,0 @@
import { useCallback } from 'react';
import { useDesignable } from '@tachybase/client';
import { GeneralField, reaction, useField, useFieldSchema } from '@tachybase/schema';
import { flatten, getValuesByPath } from '@tachybase/utils/client';
import _, { isString } from 'lodash';
import cloneDeep from 'lodash/cloneDeep';
export const useInsertSchema = (component, fieldSchema, insertAfterBegin) => {
const insert = (ss) => {
const schema = fieldSchema.reduceProperties((buf, s) => {
if (s['x-component'] === 'AssociationField.' + component) {
return s;
}
return buf;
}, null);
if (!schema) {
insertAfterBegin(cloneDeep(ss));
}
};
return insert;
};