: 移动端录单与筛选新设计 (#969)
Co-authored-by: sealday <zhanglin@daoyoucloud.com> Co-authored-by: hello@lv <2256334253@qq.com> Co-authored-by: bai.zixv <bai.zixv@foxmail.com> Reviewed-on: daoyoucloud/tachybase#951 Co-authored-by: wjh <wwwjh0710@163.com> Co-committed-by: wjh <wwwjh0710@163.com> Co-authored-by: wjh <wwwjh0710@163.com> Co-authored-by: bai.zixv <bai.zixv@foxmail.com> Reviewed-on: daoyoucloud/tachybase#969
This commit is contained in:
parent
0602743922
commit
d7b2695612
@ -35,6 +35,11 @@ import {
|
||||
usePropsRelatedImageSearchItemField,
|
||||
useTabSearchFieldItemProps,
|
||||
useTabSearchFieldItemRelatedProps,
|
||||
MInput,
|
||||
MCheckbox,
|
||||
MDatePicker,
|
||||
MRadio,
|
||||
MImageUploader,
|
||||
} from './schema';
|
||||
import './bridge';
|
||||
import './assets/svg';
|
||||
@ -70,6 +75,11 @@ export const MobileCore: React.FC = (props) => {
|
||||
ImageSearchItemView: ImageSearchItemView,
|
||||
// NoticeBlock,
|
||||
// NoticeBlockInitializer,
|
||||
MInput,
|
||||
MCheckbox,
|
||||
MDatePicker,
|
||||
MRadio,
|
||||
MImageUploader,
|
||||
}}
|
||||
scope={{
|
||||
useGridCardBlockItemProps,
|
||||
|
@ -0,0 +1,69 @@
|
||||
import { useCollectionField } from '@tachybase/client';
|
||||
import { connect, isValid, mapProps, mapReadPretty, useField } from '@tachybase/schema';
|
||||
import { Checkbox, CheckboxGroupProps, CheckboxProps, Tag } from 'antd-mobile';
|
||||
import React, { useEffect } from 'react';
|
||||
type ComposedCheckBox = React.FC<CheckboxProps> & {
|
||||
Group?: any;
|
||||
};
|
||||
export const MCheckbox: ComposedCheckBox = connect(
|
||||
Checkbox,
|
||||
mapProps((props) => {
|
||||
return { ...props };
|
||||
}),
|
||||
);
|
||||
MCheckbox.Group = connect(
|
||||
(props) => {
|
||||
const collectionField = useCollectionField();
|
||||
const dataSource = collectionField?.uiSchema.enum || [];
|
||||
const check = props.value || [];
|
||||
return (
|
||||
<Checkbox.Group value={check}>
|
||||
{dataSource.map((item, index) => {
|
||||
return (
|
||||
<Checkbox
|
||||
value={item.value}
|
||||
key={index}
|
||||
style={{ marginRight: '10px' }}
|
||||
onChange={(status) => {
|
||||
if (status) {
|
||||
check.push(item.value);
|
||||
} else {
|
||||
const itemIndex = check.findIndex((cItem) => item.value?.toString() === cItem);
|
||||
check.splice(itemIndex, 1);
|
||||
}
|
||||
props.onChange(check);
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</Checkbox>
|
||||
);
|
||||
})}
|
||||
</Checkbox.Group>
|
||||
);
|
||||
},
|
||||
mapProps((props: any, field: any) => {
|
||||
return { ...props };
|
||||
}),
|
||||
mapReadPretty((props) => {
|
||||
if (!isValid(props.value)) {
|
||||
return <div></div>;
|
||||
}
|
||||
const { value } = props;
|
||||
const field = useField<any>();
|
||||
const collectionField = useCollectionField();
|
||||
const dataSource = field.dataSource || collectionField?.uiSchema.enum || [];
|
||||
return (
|
||||
<div>
|
||||
{dataSource
|
||||
.filter((option) => option.value == value)
|
||||
.map((option, key) => (
|
||||
<Tag key={key} color={option.color}>
|
||||
{option.label}
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
export default MCheckbox;
|
@ -0,0 +1 @@
|
||||
export * from './Checkbox';
|
@ -0,0 +1,37 @@
|
||||
import { dayjs } from '@tachybase/utils/client';
|
||||
import { connect, mapProps } from '@tachybase/schema';
|
||||
import { Button, DatePicker, Input, Space } from 'antd-mobile';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export const MDatePicker = connect(
|
||||
(props) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const nowDate = props.value || new Date();
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
{dayjs(nowDate).format('YYYY-MM-DD')}
|
||||
</Button>
|
||||
|
||||
<DatePicker
|
||||
visible={visible}
|
||||
onClose={() => {
|
||||
setVisible(false);
|
||||
}}
|
||||
onConfirm={(value) => {
|
||||
props.onChange(value);
|
||||
setVisible(false);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
},
|
||||
mapProps((props) => {
|
||||
return { ...props };
|
||||
}),
|
||||
);
|
||||
export default MDatePicker;
|
@ -0,0 +1 @@
|
||||
export * from './DatePicker';
|
@ -0,0 +1,65 @@
|
||||
import { useAPIClient } from '@tachybase/client';
|
||||
import { connect, mapProps, mapReadPretty, useForm } from '@tachybase/schema';
|
||||
import { ImageUploadItem, ImageUploader } from 'antd-mobile';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
export const MImageUploader = connect(
|
||||
(props) => {
|
||||
const [fileList, setFileList] = useState<ImageUploadItem[]>([]);
|
||||
const [change, setChange] = useState(false);
|
||||
const field = props.value || [];
|
||||
useEffect(() => {
|
||||
if (!change && props.value) {
|
||||
const data = props.value.map((item) => {
|
||||
return { url: item.url, key: item.id, thumbnailUrl: item.url };
|
||||
});
|
||||
setFileList(data);
|
||||
}
|
||||
}, [props.value]);
|
||||
const api = useAPIClient();
|
||||
return (
|
||||
<ImageUploader
|
||||
value={fileList}
|
||||
upload={async (file) => {
|
||||
const { name } = file;
|
||||
const imageField = isImage(name);
|
||||
if (imageField) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
let result;
|
||||
await api
|
||||
.request({ url: props.action, method: 'post', data: formData })
|
||||
.then((res) => {
|
||||
result = res?.data?.data;
|
||||
field.push(result);
|
||||
})
|
||||
.catch(() => {});
|
||||
return {
|
||||
url: result?.url,
|
||||
key: result?.id,
|
||||
thumbnailUrl: result?.url,
|
||||
};
|
||||
}
|
||||
}}
|
||||
onChange={(file) => {
|
||||
setFileList(file);
|
||||
setChange(true);
|
||||
props.onChange(field);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
mapProps((props) => {
|
||||
return { ...props };
|
||||
}),
|
||||
);
|
||||
export default MImageUploader;
|
||||
|
||||
export const isImage = (extName: string) => {
|
||||
const reg = /\.(png|jpg|jpeg|gif|webp)$/i;
|
||||
return reg.test(extName);
|
||||
};
|
||||
|
||||
export const isPdf = (extName: string) => {
|
||||
return extName.toLowerCase().endsWith('.pdf');
|
||||
};
|
@ -0,0 +1 @@
|
||||
export * from './ImageUploader';
|
@ -0,0 +1,23 @@
|
||||
import { connect, mapProps } from '@tachybase/schema';
|
||||
import { Input, InputProps, TextArea, TextAreaProps } from 'antd-mobile';
|
||||
|
||||
type ComposedInput = React.FC<InputProps> & {
|
||||
TextArea?: React.FC<TextAreaProps>;
|
||||
};
|
||||
|
||||
export const MInput: ComposedInput = connect(
|
||||
Input,
|
||||
mapProps((props) => {
|
||||
return { placeholder: '请输入内容', clearable: true, ...props };
|
||||
}),
|
||||
);
|
||||
|
||||
const MTextArea = connect(
|
||||
TextArea,
|
||||
mapProps((props) => {
|
||||
return { placeholder: '请输入内容', ...props };
|
||||
}),
|
||||
);
|
||||
|
||||
MInput.TextArea = MTextArea;
|
||||
export default MInput;
|
@ -0,0 +1 @@
|
||||
export * from './Input';
|
@ -0,0 +1,67 @@
|
||||
import { useCollectionField } from '@tachybase/client';
|
||||
import { connect, isValid, mapProps, mapReadPretty, useField } from '@tachybase/schema';
|
||||
import { Radio, RadioGroupProps, RadioProps, Tag } from 'antd-mobile';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
type ComposedRadio = React.FC<RadioProps> & {
|
||||
Group?: any;
|
||||
};
|
||||
|
||||
export const MRadio: ComposedRadio = connect(
|
||||
Radio,
|
||||
mapProps({
|
||||
value: 'checked',
|
||||
onInput: 'onChange',
|
||||
}),
|
||||
);
|
||||
|
||||
MRadio.Group = connect(
|
||||
(props) => {
|
||||
const collectionField = useCollectionField();
|
||||
const dataSource = collectionField?.uiSchema.enum || [];
|
||||
return (
|
||||
<Radio.Group {...props}>
|
||||
{dataSource.map((item, index) => {
|
||||
return (
|
||||
<Radio value={item.value} key={index} style={{ marginRight: '10px' }}>
|
||||
{item.label}
|
||||
</Radio>
|
||||
);
|
||||
})}
|
||||
</Radio.Group>
|
||||
);
|
||||
},
|
||||
mapProps((props: any, field: any) => {
|
||||
useEffect(() => {
|
||||
const defaultOption = field.dataSource?.find((option) => option.value == props.value);
|
||||
if (defaultOption) {
|
||||
field.setValue(defaultOption.value);
|
||||
}
|
||||
}, [props.value, field.dataSource]);
|
||||
return {
|
||||
...props,
|
||||
};
|
||||
}),
|
||||
mapReadPretty((props) => {
|
||||
if (!isValid(props.value)) {
|
||||
return <div></div>;
|
||||
}
|
||||
const { value } = props;
|
||||
const field = useField<any>();
|
||||
const collectionField = useCollectionField();
|
||||
const dataSource = field.dataSource || collectionField?.uiSchema.enum || [];
|
||||
return (
|
||||
<div>
|
||||
{dataSource
|
||||
.filter((option) => option.value == value)
|
||||
.map((option, key) => (
|
||||
<Tag key={key} color={option.color}>
|
||||
{option.label}
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
export default MRadio;
|
@ -0,0 +1 @@
|
||||
export * from './Radio';
|
@ -0,0 +1,5 @@
|
||||
export * from './Input';
|
||||
export * from './Checkbox';
|
||||
export * from './DatePicker';
|
||||
export * from './ImageUploader';
|
||||
export * from './Radio';
|
@ -4,6 +4,7 @@ import React, { useEffect } from 'react';
|
||||
import { Navigate, useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
import { ContainerDesigner } from './Container.Designer';
|
||||
import useStyles from './style';
|
||||
import { MobileProvider } from '../../provider/MobileProvider';
|
||||
|
||||
const findGrid = (schema, uid) => {
|
||||
return schema.reduceProperties((final, next) => {
|
||||
@ -52,37 +53,39 @@ const InternalContainer: React.FC = (props) => {
|
||||
}, [location.pathname, navigate, params.name, redirectToUid]);
|
||||
|
||||
return (
|
||||
<SortableItem eid="nb-mobile-scroll-wrapper" className={cx('nb-mobile-container', styles.mobileContainer)}>
|
||||
<Designer></Designer>
|
||||
<div
|
||||
style={{
|
||||
paddingBottom: redirectToUid ? token.paddingLG * 2 : 0,
|
||||
}}
|
||||
className="nb-mobile-container-content"
|
||||
>
|
||||
{redirectToUid ? (
|
||||
<TabContentComponent />
|
||||
) : (
|
||||
<SchemaComponent
|
||||
filterProperties={(schema) => {
|
||||
return schema['x-component'] !== 'MTabBar';
|
||||
}}
|
||||
schema={fieldSchema}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{isTabBarEnabled && (
|
||||
<div className={cx('nb-mobile-container-tab-bar', styles.tabBar)}>
|
||||
<SchemaComponent
|
||||
onlyRenderProperties
|
||||
filterProperties={(schema) => {
|
||||
return schema['x-component'] === 'MTabBar';
|
||||
}}
|
||||
schema={fieldSchema}
|
||||
></SchemaComponent>
|
||||
<MobileProvider>
|
||||
<SortableItem eid="nb-mobile-scroll-wrapper" className={cx('nb-mobile-container', styles.mobileContainer)}>
|
||||
<Designer></Designer>
|
||||
<div
|
||||
style={{
|
||||
paddingBottom: redirectToUid ? token.paddingLG * 2 : 0,
|
||||
}}
|
||||
className="nb-mobile-container-content"
|
||||
>
|
||||
{redirectToUid ? (
|
||||
<TabContentComponent />
|
||||
) : (
|
||||
<SchemaComponent
|
||||
filterProperties={(schema) => {
|
||||
return schema['x-component'] !== 'MTabBar';
|
||||
}}
|
||||
schema={fieldSchema}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</SortableItem>
|
||||
{isTabBarEnabled && (
|
||||
<div className={cx('nb-mobile-container-tab-bar', styles.tabBar)}>
|
||||
<SchemaComponent
|
||||
onlyRenderProperties
|
||||
filterProperties={(schema) => {
|
||||
return schema['x-component'] === 'MTabBar';
|
||||
}}
|
||||
schema={fieldSchema}
|
||||
></SchemaComponent>
|
||||
</div>
|
||||
)}
|
||||
</SortableItem>
|
||||
</MobileProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
const custonComponent = {
|
||||
Input: 'MInput',
|
||||
'Input.TextArea': 'MInput.TextArea',
|
||||
'Radio.Group': 'MRadio.Group',
|
||||
'Checkbox.Group': 'MCheckbox.Group',
|
||||
'Upload.Attachment': 'MImageUploader',
|
||||
DatePicker: 'MDatePicker',
|
||||
};
|
||||
|
||||
export const canMobileField = (componentName: string) => custonComponent[componentName];
|
@ -0,0 +1,146 @@
|
||||
import {
|
||||
AssociatedFields,
|
||||
CompatibleSchemaInitializer,
|
||||
ParentCollectionFields,
|
||||
SchemaInitializerItemType,
|
||||
formItemInitializers_deprecated,
|
||||
gridRowColWrap,
|
||||
useActionContext,
|
||||
useCollectionManager_deprecated,
|
||||
useCollection_deprecated,
|
||||
} from '@tachybase/client';
|
||||
import { Schema, useForm } from '@tachybase/schema';
|
||||
import { useIsMobile } from '../../hooks';
|
||||
import { canMobileField } from './CustomComponent';
|
||||
|
||||
export const useFormItemInitializerFields = (options?: any) => {
|
||||
const { name, currentFields } = useCollection_deprecated();
|
||||
const isMobile = useIsMobile();
|
||||
const { getInterface, getCollection } = useCollectionManager_deprecated();
|
||||
const form = useForm();
|
||||
const { readPretty = form.readPretty, block = 'Form' } = options || {};
|
||||
const { fieldSchema } = useActionContext();
|
||||
const action = fieldSchema?.['x-action'];
|
||||
|
||||
return currentFields
|
||||
?.filter((field) => field?.interface && !field?.isForeignKey && !field?.treeChildren)
|
||||
?.map((field) => {
|
||||
const interfaceConfig = getInterface(field.interface);
|
||||
const targetCollection = getCollection(field.target);
|
||||
const isFileCollection = field?.target && getCollection(field?.target)?.template === 'file';
|
||||
const isAssociationField = targetCollection;
|
||||
const fieldNames = field?.uiSchema['x-component-props']?.['fieldNames'];
|
||||
const isMobileComponent = canMobileField(field.uiSchema['x-component']);
|
||||
const schema = {
|
||||
type: 'string',
|
||||
name: field.name,
|
||||
'x-toolbar': 'FormItemSchemaToolbar',
|
||||
'x-settings': 'fieldSettings:FormItem',
|
||||
'x-component': 'CollectionField',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-collection-field': `${name}.${field.name}`,
|
||||
'x-component-props': isFileCollection
|
||||
? {
|
||||
fieldNames: {
|
||||
label: 'preview',
|
||||
value: 'id',
|
||||
},
|
||||
}
|
||||
: isAssociationField && fieldNames
|
||||
? {
|
||||
fieldNames: { ...fieldNames, label: targetCollection?.titleField || fieldNames.label },
|
||||
}
|
||||
: {},
|
||||
'x-read-pretty': field?.uiSchema?.['x-read-pretty'],
|
||||
};
|
||||
if (isMobile && isMobileComponent) {
|
||||
schema['x-component-props']['component'] = isMobileComponent;
|
||||
}
|
||||
const resultItem = {
|
||||
type: 'item',
|
||||
name: field.name,
|
||||
title: field?.uiSchema?.title || field.name,
|
||||
Component: 'CollectionFieldInitializer',
|
||||
remove: removeGridFormItem,
|
||||
schemaInitialize: (s) => {
|
||||
interfaceConfig?.schemaInitialize?.(s, {
|
||||
field,
|
||||
block,
|
||||
readPretty,
|
||||
action,
|
||||
targetCollection,
|
||||
});
|
||||
},
|
||||
schema,
|
||||
} as SchemaInitializerItemType;
|
||||
if (block == 'Kanban') {
|
||||
resultItem['find'] = (schema: Schema, key: string, action: string) => {
|
||||
const s = findSchema(schema, 'x-component', block);
|
||||
return findSchema(s, key, action);
|
||||
};
|
||||
}
|
||||
|
||||
return resultItem;
|
||||
});
|
||||
};
|
||||
|
||||
export const MobileFormItemInitializers = new CompatibleSchemaInitializer(
|
||||
{
|
||||
name: 'form:configureFields',
|
||||
wrap: gridRowColWrap,
|
||||
icon: 'SettingOutlined',
|
||||
title: '{{t("Configure fields")}}',
|
||||
items: [
|
||||
{
|
||||
type: 'itemGroup',
|
||||
name: 'displayFields',
|
||||
title: '{{t("Display fields")}}',
|
||||
useChildren: useFormItemInitializerFields,
|
||||
},
|
||||
{
|
||||
name: 'parentCollectionFields',
|
||||
Component: ParentCollectionFields,
|
||||
},
|
||||
{
|
||||
name: 'associationFields',
|
||||
Component: AssociatedFields,
|
||||
},
|
||||
{
|
||||
name: 'divider',
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
name: 'addText',
|
||||
title: '{{t("Add text")}}',
|
||||
Component: 'MarkdownFormItemInitializer',
|
||||
},
|
||||
],
|
||||
},
|
||||
formItemInitializers_deprecated,
|
||||
);
|
||||
|
||||
export const removeGridFormItem = (schema, cb) => {
|
||||
cb(schema, {
|
||||
removeParentsIfNoChildren: true,
|
||||
breakRemoveOn: {
|
||||
'x-component': 'Grid',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findSchema = (schema: Schema, key: string, action: string) => {
|
||||
if (!Schema.isSchemaInstance(schema)) return null;
|
||||
return schema.reduceProperties((buf, s) => {
|
||||
if (s[key] === action) {
|
||||
return s;
|
||||
}
|
||||
if (s['x-component'] !== 'Action.Container' && s['x-component'] !== 'AssociationField.Viewer') {
|
||||
const c = findSchema(s, key, action);
|
||||
if (c) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
});
|
||||
};
|
@ -0,0 +1 @@
|
||||
export * from './MobileFormItemInitializers';
|
@ -1,7 +1,7 @@
|
||||
import { SchemaInitializer, useCollection, useCollectionManager } from '@tachybase/client';
|
||||
import { useIsMobile } from '../tab-search/components/field-item/hooks';
|
||||
import { canBeOptionalField, canBeRelatedField } from '../tab-search/utils';
|
||||
import { createSchemaImageSearchItem } from './search-item/ImageSearchItem.schema';
|
||||
import { useIsMobile } from '../../hooks';
|
||||
|
||||
export const ImageSearchConfigureFields = new SchemaInitializer({
|
||||
name: 'ImageSearchView:configureFields',
|
||||
|
@ -8,3 +8,5 @@ export * from './image-search';
|
||||
export * from './swiper';
|
||||
export * from './tab-search';
|
||||
export * from './notice';
|
||||
export * from './form';
|
||||
export * from './antd-mobile';
|
||||
|
@ -1,10 +1,11 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from '../../../../../../locale';
|
||||
import { Grid, Divider, Picker, Input, Space, ActionSheet, DatePicker, CalendarPicker } from 'antd-mobile';
|
||||
import { dayjs } from '@tachybase/utils/client';
|
||||
import { ActionSheet, Button, Calendar, Divider, Grid, Input, Picker, Popup } from 'antd-mobile';
|
||||
import { DownOutline } from 'antd-mobile-icons';
|
||||
import type { Action } from 'antd-mobile/es/components/action-sheet';
|
||||
import { changFormat, convertFormat } from '../../utils';
|
||||
import { dayjs } from '@tachybase/utils/client';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { useTranslation } from '../../../../../../locale';
|
||||
import { convertFormat } from '../../utils';
|
||||
import { css } from '@tachybase/client';
|
||||
|
||||
export const ISelect = (props) => {
|
||||
const { options, onChange, customLabelKey } = props;
|
||||
@ -59,13 +60,34 @@ export const IDatePicker = (props) => {
|
||||
const { options, value, onChange, onInputChange } = props;
|
||||
const time = value.split('&');
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [clickCount, setClickCount] = useState(0);
|
||||
const minDate = dayjs().subtract(10, 'year').toDate();
|
||||
const maxDate = dayjs().add(3, 'year').toDate();
|
||||
|
||||
const onClick = () => {
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const onChangeDate = ([start, end]) => {
|
||||
const startTime = dayjs(start).startOf('date').toISOString();
|
||||
const endTime = dayjs(end).endOf('date').toISOString();
|
||||
// TODO: 此处受上游影响,格式必须确定为这样, 时间有限,不再往上追查
|
||||
const timeString = `"${startTime}"&"${endTime}"`;
|
||||
onInputChange(timeString);
|
||||
onChange(timeString);
|
||||
|
||||
// XXX: UI 组件库这个组件是实验性组件, 此处模拟实现自动关闭浮层功能
|
||||
if (clickCount > 0 && clickCount % 2 === 1) {
|
||||
setVisible(false);
|
||||
setClickCount(0);
|
||||
} else {
|
||||
setClickCount((preClickCount) => preClickCount + 1);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid.Item span={options.length > 1 ? 3 : 4}>
|
||||
<div
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
<div onClick={onClick}>
|
||||
<Grid columns={5}>
|
||||
<Grid.Item span={2} style={{ textAlign: 'end' }}>
|
||||
{convertFormat(JSON.parse(time[0]))}
|
||||
@ -78,22 +100,24 @@ export const IDatePicker = (props) => {
|
||||
</Grid.Item>
|
||||
</Grid>
|
||||
</div>
|
||||
<CalendarPicker
|
||||
<Popup
|
||||
visible={visible}
|
||||
selectionMode="range"
|
||||
destroyOnClose
|
||||
mask
|
||||
closeOnMaskClick
|
||||
onMaskClick={() => setVisible(false)}
|
||||
onClose={() => setVisible(false)}
|
||||
onConfirm={([start, end]) => {
|
||||
const startTime = dayjs(start).startOf('date').toISOString();
|
||||
const endTime = dayjs(end).endOf('date').toISOString();
|
||||
|
||||
// TODO: 此处受上游影响,格式必须确定为这样, 时间有限,不再往上追查
|
||||
const timeString = `"${startTime}"&"${endTime}"`;
|
||||
|
||||
onInputChange(timeString);
|
||||
onChange(timeString);
|
||||
}}
|
||||
/>
|
||||
// bodyStyle={{ height: '50vh' }}
|
||||
>
|
||||
<Calendar
|
||||
selectionMode="range"
|
||||
allowClear
|
||||
min={minDate}
|
||||
max={maxDate}
|
||||
// defaultValue={defaultRange}
|
||||
onChange={onChangeDate}
|
||||
/>
|
||||
</Popup>
|
||||
</Grid.Item>
|
||||
);
|
||||
};
|
||||
|
@ -81,9 +81,3 @@ export const useTabSearchCollapsibleInputItem = () => {
|
||||
onSelected,
|
||||
};
|
||||
};
|
||||
|
||||
export const useIsMobile = () => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const isMobile = Object.values(fieldSchema.root.properties).some((value) => value['x-component'] === 'MContainer');
|
||||
return isMobile;
|
||||
};
|
||||
|
@ -14,7 +14,8 @@ import {
|
||||
import React, { useCallback } from 'react';
|
||||
import { tval } from '../../../../../locale';
|
||||
import { canBeDataField, canBeOptionalField, canBeRelatedField, canBeSearchField } from '../utils';
|
||||
import { useIsMobile } from '../components/field-item/hooks';
|
||||
import { useIsMobile } from '../../../hooks';
|
||||
import { mapToMobile } from '../mapToMobile';
|
||||
|
||||
interface ItemInterface {
|
||||
field: FieldOptions;
|
||||
@ -159,7 +160,7 @@ function getItemInput(params: ItemInterface, isMobile?: boolean) {
|
||||
// 然后排除其他类型的关联字段
|
||||
if (isAssocField(field)) return null;
|
||||
|
||||
if (!(canBeSearchField(_interface) && !canBeRelatedField(_interface) && !canBeDataField(_interface))) {
|
||||
if (!canBeSearchField(_interface) && !canBeRelatedField(_interface) && !canBeDataField(_interface)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -201,10 +202,7 @@ function getItemInput(params: ItemInterface, isMobile?: boolean) {
|
||||
'x-collection-field': `${collectionName}.${schemaName}`,
|
||||
// 'x-decorator': 'FormItem',
|
||||
// 'x-component': 'CollectionField',
|
||||
'x-component': matchTruthValue({
|
||||
['TabSearchCollapsibleInputMItem']: isMobile,
|
||||
['TabSearchCollapsibleInputItem']: !isMobile,
|
||||
}),
|
||||
'x-component': mapToMobile('TabSearchCollapsibleInputItem'),
|
||||
'x-component-props': {
|
||||
fieldNames: {
|
||||
label,
|
||||
@ -317,10 +315,7 @@ function getItemChoice(params: ItemInterface, isMobile?: boolean) {
|
||||
'x-collection-field': `${collectionName}.${schemaName}`,
|
||||
// 'x-decorator': 'FormItem',
|
||||
// 'x-component': 'CollectionField',
|
||||
'x-component': matchTruthValue({
|
||||
['TabSearchFieldMItem']: isMobile,
|
||||
['TabSearchFieldItem']: !isMobile,
|
||||
}),
|
||||
'x-component': mapToMobile('TabSearchFieldItem'),
|
||||
'x-component-props': {
|
||||
fieldNames: {
|
||||
label,
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { SchemaInitializer, useCollection, useCollectionManager } from '@tachybase/client';
|
||||
import { tval } from '../../../../../locale';
|
||||
import { useIsMobile } from '../components/field-item/hooks';
|
||||
import { createTabSearchItemSchema } from '../create/createTabSearchItemSchema';
|
||||
import { canBeDataField, canBeOptionalField, canBeRelatedField, canBeSearchField } from '../utils';
|
||||
import { TabSearchAssociatedFields } from './TabSearchAssociated.fields';
|
||||
import { useIsMobile } from '../../../hooks';
|
||||
|
||||
export const TabSearchFieldSchemaInitializer = new SchemaInitializer({
|
||||
name: 'tabSearch:configureFields',
|
||||
|
@ -0,0 +1,9 @@
|
||||
const customComponent = {
|
||||
['TabSearchCollapsibleInputItem']: 'TabSearchCollapsibleInputMItem',
|
||||
['useTabSearchFieldItemProps']: 'useTabSearchFieldItemRelatedProps',
|
||||
['TabSearchFieldItem']: 'TabSearchFieldMItem',
|
||||
};
|
||||
|
||||
export function mapToMobile(componentName: string) {
|
||||
return customComponent[componentName];
|
||||
}
|
@ -1 +1,2 @@
|
||||
export * from './useSchemaPatch';
|
||||
export * from './useIsMobile';
|
||||
|
@ -0,0 +1,7 @@
|
||||
import { useContext } from 'react';
|
||||
import { MobileContext } from '../provider/MobileProvider';
|
||||
|
||||
export const useIsMobile = () => {
|
||||
const ctx = useContext(MobileContext);
|
||||
return ctx.isMobile;
|
||||
};
|
@ -31,11 +31,11 @@ export const mBlockInitializers_deprecated = new CompatibleSchemaInitializer({
|
||||
title: '{{t("Form")}}',
|
||||
Component: 'FormBlockInitializer',
|
||||
},
|
||||
// {
|
||||
// name: 'details',
|
||||
// title: '{{t("Details")}}',
|
||||
// Component: 'DetailsBlockInitializer',
|
||||
// },
|
||||
{
|
||||
name: 'details',
|
||||
title: '{{t("Details")}}',
|
||||
Component: 'DetailsBlockInitializer',
|
||||
},
|
||||
{
|
||||
name: 'calendar',
|
||||
title: '{{t("Calendar")}}',
|
||||
@ -96,11 +96,11 @@ export const mBlockInitializers = new CompatibleSchemaInitializer(
|
||||
title: '{{t("Table")}}',
|
||||
Component: 'TableBlockInitializer',
|
||||
},
|
||||
// {
|
||||
// name: 'form',
|
||||
// title: '{{t("Form")}}',
|
||||
// Component: 'FormBlockInitializer',
|
||||
// },
|
||||
{
|
||||
name: 'form',
|
||||
title: '{{t("Form")}}',
|
||||
Component: 'FormBlockInitializer',
|
||||
},
|
||||
{
|
||||
name: 'details',
|
||||
title: '{{t("Details")}}',
|
||||
|
@ -0,0 +1,7 @@
|
||||
import React, { createContext } from 'react';
|
||||
|
||||
export const MobileContext = createContext<any>({});
|
||||
|
||||
export const MobileProvider = (props) => {
|
||||
return <MobileContext.Provider value={{ isMobile: true }}> {props.children}</MobileContext.Provider>;
|
||||
};
|
@ -8,6 +8,7 @@ import {
|
||||
ImageSearchItemFieldSettings,
|
||||
mBlockInitializers,
|
||||
mBlockInitializers_deprecated,
|
||||
MobileFormItemInitializers,
|
||||
TabSearchFieldSchemaInitializer,
|
||||
TabSearchItemFieldSettings,
|
||||
} from './core/schema';
|
||||
@ -27,7 +28,8 @@ export class MobileClientPlugin extends Plugin {
|
||||
this.app.schemaInitializerManager.add(TabSearchFieldSchemaInitializer);
|
||||
|
||||
this.app.schemaSettingsManager.add(ImageSearchItemFieldSettings);
|
||||
this.schemaSettingsManager.add(TabSearchItemFieldSettings);
|
||||
this.app.schemaSettingsManager.add(TabSearchItemFieldSettings);
|
||||
this.app.schemaInitializerManager.add(MobileFormItemInitializers);
|
||||
}
|
||||
|
||||
addSettings() {
|
||||
|
Loading…
Reference in New Issue
Block a user