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 { ApprovalTemplateType } from '../../component/ApprovalTemplateType';
import { PendingStatus } from '../../constants';
import { useTodosContext } from '../provider/todosContext';
import { TabApplicantType } from './TabApplicantType';
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 { 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 { MInput } from '../Input';
import './style';
import {
BlockItem,
CollectionProvider_deprecated,
CollectionProvider,
SchemaComponent,
useAPIClient,
useCollection,
useCollectionField,
useCollectionManager,
useDesignable,
useRequest,
} from '@tachybase/client';
import { isArray } from '@tachybase/utils/client';
import { getMobileColor } from '../../../CustomColor';
import { MobileProvider } from '../../../provider';
import { MInput } from '../Input';
import { CreateRecordAction } from './CreateRecordAction';
import { useInsertSchema } from './hook/hooks';
import schema from './schema';
import { useStyles } from './style';
export const AntdSelect = observer((props) => {
@ -30,13 +32,21 @@ export const AntdSelect = observer((props) => {
const [options, setOptions] = useState([]);
const fieldSchema = useFieldSchema();
const cm = useCollectionManager();
const [visibleValue, setVisibleValue] = useState({});
const collection = useCollection();
const [filter, setFilter] = useState(service?.params?.filter);
const api = useAPIClient();
const field = useField();
const { insertAfterBegin } = useDesignable();
const insertAddNewer = useInsertSchema('AddNewer', fieldSchema, insertAfterBegin);
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(
{
resource: collectionName,
@ -57,8 +67,8 @@ export const AntdSelect = observer((props) => {
const dataOption = data['data']?.map((value) => {
return {
...value,
label: value[fieldNames.label],
value: value[fieldNames.value],
label: value[fieldNamesLabel],
value: value[fieldNamesValue],
};
});
setOptions(dataOption);
@ -71,13 +81,12 @@ export const AntdSelect = observer((props) => {
} else {
const field = cm.getCollectionField(fieldSchema['x-collection-field']);
const data = field.uiSchema?.enum;
setVisibleValue(data.find((item) => item.value === value) || '');
setOptions(data);
}
};
const addData = () => {
api
const addData = (data) => {
data[fieldNamesLabel] = api
.request({
url: collectionName + ':create',
method: 'post',
@ -96,7 +105,7 @@ export const AntdSelect = observer((props) => {
});
}
const paramsFilter = { ...filter };
delete paramsFilter[fieldNames.label];
delete paramsFilter[fieldNamesLabel];
setFilter(paramsFilter);
setSearchValue('');
setPopupVisible(false);
@ -107,131 +116,108 @@ export const AntdSelect = observer((props) => {
};
const quickAdd = () => {
const data = {};
data[fieldNames.label] = searchValue;
addData();
};
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,
});
data[fieldNamesLabel] = searchValue;
addData(data);
};
return (
<BlockItem>
{!value || typeof value === 'object' ? (
<CollectionProvider name={collectionName || collection.name}>
<BlockItem>
<div
onClick={() => {
if (fieldSchema['x-disabled']) return;
setPopupVisible(true);
checkedPopup();
}}
style={{ color: '#c5c5c5' }}
>
{isArray(value) && value.length ? (
value.map((item, index) => (
<Space key={index}>{`${item.label}${value.length - 1 === index ? '' : ','}`}</Space>
))
{value?.length || (value && !isArray(value)) ? (
<MInput value={inputValue} disabled={fieldSchema['x-disabled']} />
) : (
<Space>{value?.label || '请选择内容'}</Space>
<Space>{fieldSchema['x-disabled'] ? '' : '请选择内容'}</Space>
)}
</div>
) : (
<Tag color={'default'}>{value?.['label']}</Tag>
)}
<Popup visible={popupVisible} className={`${styles['PopupStyle']}`} closeOnMaskClick>
<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>
<SearchBar
placeholder="请输入内容"
value={searchValue}
onChange={(value) => {
const paramsFilter = { ...filter };
paramsFilter[fieldNames.label] = { $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);
}}
/>
)}
<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>
<Space justify="evenly" align="center">
{addMode === 'modalAdd' ? (
<SchemaComponent
components={{ CreateRecordAction }}
schema={{
type: 'void',
name: 'CreateRecord',
'x-component': 'CreateRecordAction',
'x-component-props': { fieldSchema },
}}
/>
) : null}
<Button
color="primary"
onClick={() => {
setPopupVisible(false);
}}
>
</Button>
<Button
color="primary"
onClick={() => {
onChange(selectValue);
setPopupVisible(false);
}}
>
</Button>
</Space>
</MobileProvider>
</Popup>
</BlockItem>
</CollectionProvider>
);
});

View File

@ -1,29 +1,55 @@
import React, { useState } from 'react';
import {
CollectionProvider,
CollectionProvider_deprecated,
useActionContext,
useCollectionManager,
useCollectionManager_deprecated,
} from '@tachybase/client';
import { RecursionField } from '@tachybase/schema';
import { css, SchemaComponentOptions, useApp, useCollection } from '@tachybase/client';
import { observer, RecursionField, useField, useFieldSchema } from '@tachybase/schema';
import { useInsertSchema } from './hook/hooks';
import schema from './schema';
import { Button, CenterPopup, Modal, Popup } from 'antd-mobile';
export const CreateRecordAction = ({ field, fieldSchema, targetCollection }) => {
const [currentCollection, setCurrentCollection] = useState(targetCollection?.name);
const [currentDataSource, setCurrentDataSource] = useState(targetCollection?.dataSource);
return (
<CollectionProvider_deprecated name={currentCollection} dataSource={currentDataSource}>
<RecursionField
onlyRenderProperties
basePath={field.address}
schema={fieldSchema}
filterProperties={(s) => {
return s['x-component'] === 'AssociationField.AddNewer';
}}
/>
</CollectionProvider_deprecated>
);
};
import { MobileProvider } from '../../../provider';
export const CreateRecordAction = observer(
(props) => {
const { fieldSchema } = props as any;
const [visible, setVisible] = useState(false);
const addNew = fieldSchema.reduceProperties((buf, schema) => {
const found = schema.reduceProperties((buf, schema) => {
if (schema['x-component'] === 'AssociationField.AddNewer') {
return schema;
}
return buf;
});
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 cm = useCollectionManager();
const collection = cm.getCollection(fieldSchema['x-collection-field']);
const field = useField<any>();
const collectionField = useCollectionField();
const dataSource = field.dataSource || collectionField?.uiSchema.enum || [];
if (collection) {
filterProps['collectionName'] = collection.name;
}
if (typeof filterProps['value'] !== 'object') {
filterProps['value'] = dataSource.filter((item) => item.value.toString() === filterProps['value']);
}
return { ...filterProps };
}),
mapReadPretty((props) => {
const { value, fieldNames } = props;
const isCollectionField = typeof value === 'object' && fieldNames;
if (isCollectionField) {
const isArrayField = isArray(value);
return isArrayField ? (
<div>
{value.map((item, index) => (
<Space key={index}>{`${item?.[fieldNames.label] || ''}${value.length - 1 === index ? '' : ','}`}</Space>
))}
</div>
) : (
<Space>{value?.[fieldNames.label] || ''}</Space>
);
} else {
const field = useField<any>();
const collectionField = useCollectionField();
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);
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>
{options.map((option, key) => (
<Tag key={key} color={getMobileColor(option.color)} style={{ margin: '5px' }}>
{option.label}
{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) => {
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;
};