From d7b2695612be1bbf0f0b53b244b6c737e2356513 Mon Sep 17 00:00:00 2001 From: sealday Date: Wed, 15 May 2024 11:45:42 +0800 Subject: [PATCH] =?UTF-8?q?:=20=E7=A7=BB=E5=8A=A8=E7=AB=AF=E5=BD=95?= =?UTF-8?q?=E5=8D=95=E4=B8=8E=E7=AD=9B=E9=80=89=E6=96=B0=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=20(#969)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: sealday Co-authored-by: hello@lv <2256334253@qq.com> Co-authored-by: bai.zixv Reviewed-on: https://git.daoyoucloud.com/daoyoucloud/tachybase/pulls/951 Co-authored-by: wjh Co-committed-by: wjh Co-authored-by: wjh Co-authored-by: bai.zixv Reviewed-on: https://git.daoyoucloud.com/daoyoucloud/tachybase/pulls/969 --- .../src/client/core/index.tsx | 10 ++ .../antd-mobile/Checkbox/Checkbox.tsx | 69 +++++++++ .../components/antd-mobile/Checkbox/index.ts | 1 + .../antd-mobile/DatePicker/DatePicker.tsx | 37 +++++ .../antd-mobile/DatePicker/index.ts | 1 + .../ImageUploader/ImageUploader.tsx | 65 ++++++++ .../antd-mobile/ImageUploader/index.ts | 1 + .../components/antd-mobile/Input/Input.tsx | 23 +++ .../components/antd-mobile/Input/index.ts | 1 + .../components/antd-mobile/Radio/Radio.tsx | 67 ++++++++ .../components/antd-mobile/Radio/index.ts | 1 + .../schema/components/antd-mobile/index.ts | 5 + .../schema/components/container/Container.tsx | 63 ++++---- .../components/form/CustomComponent.tsx | 10 ++ .../form/MobileFormItemInitializers.tsx | 146 ++++++++++++++++++ .../core/schema/components/form/index.ts | 1 + .../image-search/ImageSearch.configure.ts | 2 +- .../client/core/schema/components/index.ts | 2 + .../TabSearchCollapsibleInputMItemChild.tsx | 70 ++++++--- .../components/field-item/hooks.tsx | 6 - .../TabSearchAssociated.fields.tsx | 15 +- .../TabSearchFieldSchemaInitializer.tsx | 2 +- .../components/tab-search/mapToMobile.ts | 9 ++ .../src/client/core/schema/hooks/index.ts | 1 + .../client/core/schema/hooks/useIsMobile.ts | 7 + .../schema/initializers/BlockInitializers.ts | 20 +-- .../core/schema/provider/MobileProvider.tsx | 7 + .../plugin-mobile-client/src/client/index.tsx | 4 +- 28 files changed, 564 insertions(+), 82 deletions(-) create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Checkbox/Checkbox.tsx create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Checkbox/index.ts create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/DatePicker/DatePicker.tsx create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/DatePicker/index.ts create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/ImageUploader/ImageUploader.tsx create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/ImageUploader/index.ts create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Input/Input.tsx create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Input/index.ts create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Radio/Radio.tsx create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Radio/index.ts create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/index.ts create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/form/CustomComponent.tsx create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/form/MobileFormItemInitializers.tsx create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/form/index.ts create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/mapToMobile.ts create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/hooks/useIsMobile.ts create mode 100644 packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/provider/MobileProvider.tsx diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/index.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/index.tsx index 0ec75ad29..a1a58124b 100644 --- a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/index.tsx +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/index.tsx @@ -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, diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Checkbox/Checkbox.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Checkbox/Checkbox.tsx new file mode 100644 index 000000000..da567a3c0 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Checkbox/Checkbox.tsx @@ -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 & { + 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 ( + + {dataSource.map((item, index) => { + return ( + { + 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} + + ); + })} + + ); + }, + mapProps((props: any, field: any) => { + return { ...props }; + }), + mapReadPretty((props) => { + if (!isValid(props.value)) { + return
; + } + const { value } = props; + const field = useField(); + const collectionField = useCollectionField(); + const dataSource = field.dataSource || collectionField?.uiSchema.enum || []; + return ( +
+ {dataSource + .filter((option) => option.value == value) + .map((option, key) => ( + + {option.label} + + ))} +
+ ); + }), +); + +export default MCheckbox; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Checkbox/index.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Checkbox/index.ts new file mode 100644 index 000000000..f5c939faf --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Checkbox/index.ts @@ -0,0 +1 @@ +export * from './Checkbox'; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/DatePicker/DatePicker.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/DatePicker/DatePicker.tsx new file mode 100644 index 000000000..0166de9f1 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/DatePicker/DatePicker.tsx @@ -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 ( + <> + + + { + setVisible(false); + }} + onConfirm={(value) => { + props.onChange(value); + setVisible(false); + }} + /> + + ); + }, + mapProps((props) => { + return { ...props }; + }), +); +export default MDatePicker; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/DatePicker/index.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/DatePicker/index.ts new file mode 100644 index 000000000..a48b62e4d --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/DatePicker/index.ts @@ -0,0 +1 @@ +export * from './DatePicker'; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/ImageUploader/ImageUploader.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/ImageUploader/ImageUploader.tsx new file mode 100644 index 000000000..37061eb0f --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/ImageUploader/ImageUploader.tsx @@ -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([]); + 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 ( + { + 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'); +}; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/ImageUploader/index.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/ImageUploader/index.ts new file mode 100644 index 000000000..0391b7ee8 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/ImageUploader/index.ts @@ -0,0 +1 @@ +export * from './ImageUploader'; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Input/Input.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Input/Input.tsx new file mode 100644 index 000000000..58976e897 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Input/Input.tsx @@ -0,0 +1,23 @@ +import { connect, mapProps } from '@tachybase/schema'; +import { Input, InputProps, TextArea, TextAreaProps } from 'antd-mobile'; + +type ComposedInput = React.FC & { + TextArea?: React.FC; +}; + +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; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Input/index.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Input/index.ts new file mode 100644 index 000000000..ba9fe7ebc --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Input/index.ts @@ -0,0 +1 @@ +export * from './Input'; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Radio/Radio.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Radio/Radio.tsx new file mode 100644 index 000000000..770b75641 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Radio/Radio.tsx @@ -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 & { + 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 ( + + {dataSource.map((item, index) => { + return ( + + {item.label} + + ); + })} + + ); + }, + 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
; + } + const { value } = props; + const field = useField(); + const collectionField = useCollectionField(); + const dataSource = field.dataSource || collectionField?.uiSchema.enum || []; + return ( +
+ {dataSource + .filter((option) => option.value == value) + .map((option, key) => ( + + {option.label} + + ))} +
+ ); + }), +); + +export default MRadio; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Radio/index.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Radio/index.ts new file mode 100644 index 000000000..bfbe6d09b --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/Radio/index.ts @@ -0,0 +1 @@ +export * from './Radio'; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/index.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/index.ts new file mode 100644 index 000000000..3e66753eb --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/antd-mobile/index.ts @@ -0,0 +1,5 @@ +export * from './Input'; +export * from './Checkbox'; +export * from './DatePicker'; +export * from './ImageUploader'; +export * from './Radio'; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/container/Container.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/container/Container.tsx index 86d12f679..2789ef91f 100644 --- a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/container/Container.tsx +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/container/Container.tsx @@ -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 ( - - -
- {redirectToUid ? ( - - ) : ( - { - return schema['x-component'] !== 'MTabBar'; - }} - schema={fieldSchema} - /> - )} -
- {isTabBarEnabled && ( -
- { - return schema['x-component'] === 'MTabBar'; - }} - schema={fieldSchema} - > + + + +
+ {redirectToUid ? ( + + ) : ( + { + return schema['x-component'] !== 'MTabBar'; + }} + schema={fieldSchema} + /> + )}
- )} -
+ {isTabBarEnabled && ( +
+ { + return schema['x-component'] === 'MTabBar'; + }} + schema={fieldSchema} + > +
+ )} + +
); }; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/form/CustomComponent.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/form/CustomComponent.tsx new file mode 100644 index 000000000..eb554c69a --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/form/CustomComponent.tsx @@ -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]; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/form/MobileFormItemInitializers.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/form/MobileFormItemInitializers.tsx new file mode 100644 index 000000000..eb07bfa77 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/form/MobileFormItemInitializers.tsx @@ -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; + }); +}; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/form/index.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/form/index.ts new file mode 100644 index 000000000..9dc912b5b --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/form/index.ts @@ -0,0 +1 @@ +export * from './MobileFormItemInitializers'; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/image-search/ImageSearch.configure.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/image-search/ImageSearch.configure.ts index ad7dd8214..937e28995 100644 --- a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/image-search/ImageSearch.configure.ts +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/image-search/ImageSearch.configure.ts @@ -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', diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/index.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/index.ts index 23f344029..30b9cb44c 100644 --- a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/index.ts +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/index.ts @@ -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'; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/components/field-item/TabSearchCollapsibleInputMItemChild.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/components/field-item/TabSearchCollapsibleInputMItemChild.tsx index 25e25cf30..68aa507ec 100644 --- a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/components/field-item/TabSearchCollapsibleInputMItemChild.tsx +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/components/field-item/TabSearchCollapsibleInputMItemChild.tsx @@ -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 ( 1 ? 3 : 4}> -
{ - setVisible(true); - }} - > +
{convertFormat(JSON.parse(time[0]))} @@ -78,22 +100,24 @@ export const IDatePicker = (props) => {
- 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' }} + > + + ); }; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/components/field-item/hooks.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/components/field-item/hooks.tsx index 1822e817a..5e7379dd1 100644 --- a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/components/field-item/hooks.tsx +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/components/field-item/hooks.tsx @@ -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; -}; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/initializer/TabSearchAssociated.fields.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/initializer/TabSearchAssociated.fields.tsx index 93b136fc7..193b0544a 100644 --- a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/initializer/TabSearchAssociated.fields.tsx +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/initializer/TabSearchAssociated.fields.tsx @@ -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, diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/initializer/TabSearchFieldSchemaInitializer.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/initializer/TabSearchFieldSchemaInitializer.tsx index 7766b8733..b510f0a01 100644 --- a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/initializer/TabSearchFieldSchemaInitializer.tsx +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/initializer/TabSearchFieldSchemaInitializer.tsx @@ -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', diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/mapToMobile.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/mapToMobile.ts new file mode 100644 index 000000000..ded4fdba2 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/tab-search/mapToMobile.ts @@ -0,0 +1,9 @@ +const customComponent = { + ['TabSearchCollapsibleInputItem']: 'TabSearchCollapsibleInputMItem', + ['useTabSearchFieldItemProps']: 'useTabSearchFieldItemRelatedProps', + ['TabSearchFieldItem']: 'TabSearchFieldMItem', +}; + +export function mapToMobile(componentName: string) { + return customComponent[componentName]; +} diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/hooks/index.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/hooks/index.ts index 414110151..2ff1ae06e 100644 --- a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/hooks/index.ts +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/hooks/index.ts @@ -1 +1,2 @@ export * from './useSchemaPatch'; +export * from './useIsMobile'; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/hooks/useIsMobile.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/hooks/useIsMobile.ts new file mode 100644 index 000000000..510295c6c --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/hooks/useIsMobile.ts @@ -0,0 +1,7 @@ +import { useContext } from 'react'; +import { MobileContext } from '../provider/MobileProvider'; + +export const useIsMobile = () => { + const ctx = useContext(MobileContext); + return ctx.isMobile; +}; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/initializers/BlockInitializers.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/initializers/BlockInitializers.ts index 94ed86365..392dae2c5 100644 --- a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/initializers/BlockInitializers.ts +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/initializers/BlockInitializers.ts @@ -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")}}', diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/provider/MobileProvider.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/provider/MobileProvider.tsx new file mode 100644 index 000000000..55694e04a --- /dev/null +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/provider/MobileProvider.tsx @@ -0,0 +1,7 @@ +import React, { createContext } from 'react'; + +export const MobileContext = createContext({}); + +export const MobileProvider = (props) => { + return {props.children}; +}; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/index.tsx b/packages/plugins/@tachybase/plugin-mobile-client/src/client/index.tsx index aec750465..62123d384 100644 --- a/packages/plugins/@tachybase/plugin-mobile-client/src/client/index.tsx +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/index.tsx @@ -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() {