feat: 移动端筛选区块二期:支持更多类型 (#702)
Reviewed-on: daoyoucloud/tachycode#702 Co-authored-by: wjh <wwwjh0710@163.com> Co-committed-by: wjh <wwwjh0710@163.com>
This commit is contained in:
parent
ec406f25ef
commit
a1223960e2
5
.changeset/friendly-dingos-call.md
Normal file
5
.changeset/friendly-dingos-call.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"@hera/plugin-mobile": patch
|
||||
---
|
||||
|
||||
新增mobile筛选类型
|
@ -73,7 +73,7 @@ export const SwiperFieldSettings = new SchemaSettings({
|
||||
dn.refresh();
|
||||
};
|
||||
return {
|
||||
title: t('SetShowfield'),
|
||||
title: t('Set show field'),
|
||||
options,
|
||||
value: fieldSchema['x-component-props'].fieldValue,
|
||||
onChange,
|
||||
@ -86,6 +86,7 @@ export const SwiperFieldSettings = new SchemaSettings({
|
||||
useComponentProps() {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { dn } = useDesignable();
|
||||
const { t } = useTranslation();
|
||||
const options = [];
|
||||
for (let i = 1; i <= 20; i++) {
|
||||
options.push({
|
||||
@ -104,7 +105,7 @@ export const SwiperFieldSettings = new SchemaSettings({
|
||||
dn.refresh();
|
||||
};
|
||||
return {
|
||||
title: 'setFieldCount',
|
||||
title: t('Set field count'),
|
||||
options,
|
||||
value: fieldSchema['x-component-props'].pageSize || 5,
|
||||
onChange,
|
||||
|
@ -1,36 +1,74 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useFieldSchema } from '@nocobase/schema';
|
||||
import { useTranslation } from '../../../../locale';
|
||||
import { useCollection, useDesigner } from '@nocobase/client';
|
||||
import { isTabSearchCollapsibleInputItem } from '../../utils';
|
||||
import { useCollection, useCollectionManager, useDesignable, useDesigner } from '@nocobase/client';
|
||||
import { canBeDataField, canBeRelatedField, convertFormat, isTabSearchCollapsibleInputItem } from '../../utils';
|
||||
import { useTabSearchCollapsibleInputItem } from './hooks';
|
||||
import { dayjs } from '@nocobase/utils/client';
|
||||
|
||||
export const useTabSearchCollapsibleInputItemAction = (props) => {
|
||||
const { onSelected } = useTabSearchCollapsibleInputItem();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const collection = useCollection();
|
||||
const cm = useCollectionManager();
|
||||
const { dn } = useDesignable();
|
||||
const collectionField = useMemo(() => collection?.getField(fieldSchema.name as any), [collection, fieldSchema.name]);
|
||||
const properties = fieldSchema.parent.properties;
|
||||
const Designer = useDesigner();
|
||||
const [value, setValue] = useState('');
|
||||
const [customLabelKey, setCustomLabelKey] = useState(fieldSchema['name'] as string);
|
||||
|
||||
const [fieldInterface, setFieldInterface] = useState(fieldSchema['x-component-props'].interface);
|
||||
const defaultValue = canBeDataField(fieldInterface)
|
||||
? convertFormat(new Date()) + '&' + convertFormat(new Date())
|
||||
: '';
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
const options = Object.values(properties)
|
||||
.map((option) => {
|
||||
if (isTabSearchCollapsibleInputItem(option['x-component'])) {
|
||||
return {
|
||||
label: option['title'],
|
||||
value: option['name'],
|
||||
interface: option['x-component-props'].interface,
|
||||
};
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
const onSelect = (value) => {
|
||||
onSelected([value], customLabelKey);
|
||||
let time;
|
||||
let filterKey = `${customLabelKey}.$includes`;
|
||||
if (canBeDataField(fieldInterface)) {
|
||||
time = value.split('&');
|
||||
filterKey = `${customLabelKey}.$dateBetween`;
|
||||
}
|
||||
if (canBeRelatedField(fieldInterface)) {
|
||||
const label = fieldSchema['x-component-props']['fieldNames'].label;
|
||||
const correlation = fieldSchema['x-component-props']['correlation'];
|
||||
filterKey = `${correlation}.${label}.$includes`;
|
||||
}
|
||||
onSelected(time || [value], filterKey);
|
||||
};
|
||||
|
||||
const onSelectChange = (label) => setCustomLabelKey(label as string);
|
||||
const onSelectChange = (label) => {
|
||||
const type = Object.values(properties).find((value) => value.name === label)['x-component-props'].interface;
|
||||
if (canBeRelatedField(type)) {
|
||||
const titleField = cm.getCollection(collection.name + '.' + label).titleField;
|
||||
fieldSchema['x-component-props']['correlation'] = label;
|
||||
fieldSchema['x-component-props']['fieldNames'] = { label: titleField };
|
||||
dn.emit('patch', {
|
||||
schema: {
|
||||
['u-id']: fieldSchema['u-id'],
|
||||
['x-component-props']: fieldSchema['x-component-props'],
|
||||
},
|
||||
});
|
||||
dn.refresh();
|
||||
}
|
||||
setFieldInterface(type);
|
||||
if (canBeDataField(type)) {
|
||||
setValue(convertFormat(new Date()) + '&' + convertFormat(new Date()));
|
||||
} else {
|
||||
setValue('');
|
||||
}
|
||||
setCustomLabelKey(label as string);
|
||||
};
|
||||
const onInputChange = (v) => {
|
||||
const inputValue = v.target?.value || v;
|
||||
setValue(inputValue);
|
||||
@ -41,6 +79,20 @@ export const useTabSearchCollapsibleInputItemAction = (props) => {
|
||||
const onButtonClick = () => {
|
||||
onSelect(value);
|
||||
};
|
||||
const onDateClick = (time) => {
|
||||
onSelect(time);
|
||||
};
|
||||
|
||||
return { collectionField, Designer, options, value, onSelectChange, onInputChange, onButtonClick, customLabelKey };
|
||||
return {
|
||||
collectionField,
|
||||
Designer,
|
||||
options,
|
||||
value,
|
||||
onSelectChange,
|
||||
onInputChange,
|
||||
onButtonClick,
|
||||
customLabelKey,
|
||||
fieldInterface,
|
||||
onDateClick,
|
||||
};
|
||||
};
|
||||
|
@ -2,24 +2,40 @@ import { SortableItem, withDynamicSchemaProps } from '@nocobase/client';
|
||||
import { Grid } from 'antd-mobile';
|
||||
import React from 'react';
|
||||
import { useTabSearchCollapsibleInputItemAction } from './TabSearchCollapsibleInputItemAction';
|
||||
import { IButton, IInput, ISelect } from './TabSearchCollapsibleInputMItemChild';
|
||||
import { IButton, IDatePicker, IInput, ISelect } from './TabSearchCollapsibleInputMItemChild';
|
||||
import { canBeDataField } from '../../utils';
|
||||
|
||||
export const TabSearchCollapsibleInputMItem = withDynamicSchemaProps(
|
||||
(props) => {
|
||||
const { collectionField, Designer, options, value, onSelectChange, onInputChange, onButtonClick, customLabelKey } =
|
||||
useTabSearchCollapsibleInputItemAction(props);
|
||||
const {
|
||||
collectionField,
|
||||
Designer,
|
||||
options,
|
||||
value,
|
||||
onSelectChange,
|
||||
onInputChange,
|
||||
onButtonClick,
|
||||
customLabelKey,
|
||||
fieldInterface,
|
||||
onDateClick,
|
||||
} = useTabSearchCollapsibleInputItemAction(props);
|
||||
|
||||
if (!collectionField) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SortableItem>
|
||||
<Designer />
|
||||
<Grid columns={4} style={{ backgroundColor: '#f9f9f9', borderRadius: '5px', margin: '5px 0 0 0' }}>
|
||||
<ISelect options={options} onChange={onSelectChange} customLabelKey={customLabelKey} />
|
||||
<IInput options={options} value={value} onChange={onInputChange} />
|
||||
<IButton onClick={onButtonClick} />
|
||||
{canBeDataField(fieldInterface) ? (
|
||||
<IDatePicker value={value} onChange={onDateClick} onInputChange={onInputChange} options={options} />
|
||||
) : (
|
||||
<>
|
||||
<IInput options={options} value={value} onChange={onInputChange} />
|
||||
<IButton onClick={onButtonClick} />
|
||||
</>
|
||||
)}
|
||||
</Grid>
|
||||
</SortableItem>
|
||||
);
|
||||
|
@ -1,8 +1,9 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from '../../../../locale';
|
||||
import { Grid, Divider, Picker, Input, Space, ActionSheet } from 'antd-mobile';
|
||||
import { Grid, Divider, Picker, Input, Space, ActionSheet, DatePicker, CalendarPicker } from 'antd-mobile';
|
||||
import { DownOutline } from 'antd-mobile-icons';
|
||||
import type { Action } from 'antd-mobile/es/components/action-sheet';
|
||||
import { convertFormat } from '../../utils';
|
||||
|
||||
export const ISelect = (props) => {
|
||||
const { options, onChange, customLabelKey } = props;
|
||||
@ -53,6 +54,43 @@ export const ISelect = (props) => {
|
||||
) : null;
|
||||
};
|
||||
|
||||
export const IDatePicker = (props) => {
|
||||
const { options, value, onChange, onInputChange } = props;
|
||||
const time = value.split('&');
|
||||
const [visible, setVisible] = useState(false);
|
||||
return (
|
||||
<Grid.Item span={options.length > 1 ? 3 : 4}>
|
||||
<div
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
<Grid columns={5}>
|
||||
<Grid.Item span={2} style={{ textAlign: 'end' }}>
|
||||
{time[0]}
|
||||
</Grid.Item>
|
||||
<Grid.Item span={1} style={{ textAlign: 'center' }}>
|
||||
-
|
||||
</Grid.Item>
|
||||
<Grid.Item span={2} style={{ textAlign: 'start' }}>
|
||||
{time[1]}
|
||||
</Grid.Item>
|
||||
</Grid>
|
||||
</div>
|
||||
<CalendarPicker
|
||||
visible={visible}
|
||||
selectionMode="range"
|
||||
onMaskClick={() => setVisible(false)}
|
||||
onClose={() => setVisible(false)}
|
||||
onConfirm={(v) => {
|
||||
onInputChange(convertFormat(v[0]) + '&' + convertFormat(v[1]));
|
||||
onChange(convertFormat(v[0]) + '&' + convertFormat(v[1]));
|
||||
}}
|
||||
/>
|
||||
</Grid.Item>
|
||||
);
|
||||
};
|
||||
|
||||
export const IInput = (props) => {
|
||||
const { options, value, onChange } = props;
|
||||
const { t } = useTranslation();
|
||||
@ -71,7 +109,6 @@ export const IInput = (props) => {
|
||||
export const IButton = (props) => {
|
||||
const { onClick } = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Grid.Item onClick={onClick} style={{ color: '#2c6eff', textAlign: 'center', cursor: 'pointer' }}>
|
||||
{t('Search')}
|
||||
|
@ -15,7 +15,7 @@ export const useTabSearchFieldItemAction = (props) => {
|
||||
const labelKey = _labelKey || fieldSchema['x-component-props']?.fieldNames?.label || valueKey;
|
||||
const onSelect = (itemKey) => {
|
||||
const key = itemKey.keyPath?.[0] || itemKey;
|
||||
onSelected([key], null, filterKey);
|
||||
onSelected([key], filterKey);
|
||||
};
|
||||
const fieldNames = {
|
||||
title: labelKey || valueKey,
|
||||
|
@ -8,7 +8,6 @@ import { canBeOptionalField } from '../../utils';
|
||||
export const useTabSearchFieldItemProps = () => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const collection = useCollection();
|
||||
|
||||
const optionalFieldList = useOptionalFieldList();
|
||||
const cm = useCollectionManager();
|
||||
const collectionField = useMemo(() => collection?.getField(fieldSchema.name as any), [collection, fieldSchema.name]);
|
||||
|
@ -9,16 +9,13 @@ export const useTabSearchCollapsibleInputItem = () => {
|
||||
const { getDataBlocks } = useFilterBlock();
|
||||
const collectionField = useMemo(() => collection?.getField(fieldSchema.name as any), [collection, fieldSchema.name]);
|
||||
|
||||
const onSelected = (value, customLabelKey?, filterKey?) => {
|
||||
const onSelected = (value, filterKey) => {
|
||||
const { targets, uid } = findFilterTargets(fieldSchema);
|
||||
getDataBlocks().forEach((block) => {
|
||||
const target = targets.find((target) => target.uid === block.uid);
|
||||
if (!target) return;
|
||||
const key = `${uid}${fieldSchema.name}`;
|
||||
const param = block.service.params?.[0] || {};
|
||||
if (!collectionField?.target && customLabelKey) {
|
||||
filterKey = `${customLabelKey}.$includes`;
|
||||
}
|
||||
// 保留原有的 filter
|
||||
let storedFilter = block.service.params?.[1]?.filters || {};
|
||||
if (value.length) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
export const createTabSearchItemSchema = (options) => {
|
||||
const { field, itemComponent, label, itemUseProps } = options;
|
||||
const { field, itemComponent, label, itemUseProps, collection } = options;
|
||||
return {
|
||||
name: field.key,
|
||||
title: field.uiSchema?.title,
|
||||
@ -17,6 +17,8 @@ export const createTabSearchItemSchema = (options) => {
|
||||
label,
|
||||
},
|
||||
interface: field.interface,
|
||||
collectionName: collection?.name,
|
||||
correlation: field.name,
|
||||
},
|
||||
properties: {},
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { SchemaInitializer, SchemaInitializerItemType, useCollection, useCollectionManager } from '@nocobase/client';
|
||||
import _ from 'lodash';
|
||||
import { tval } from '../../../locale';
|
||||
import { canBeOptionalField, canBeRelatedField, canBeSearchField } from '../utils';
|
||||
import { canBeDataField, canBeOptionalField, canBeRelatedField, canBeSearchField } from '../utils';
|
||||
import { createTabSearchItemSchema } from '../create/createTabSearchItemSchema';
|
||||
import { useIsMobile } from '../components/field-item/hooks';
|
||||
|
||||
@ -12,13 +12,19 @@ const textFieldsItem: SchemaInitializerItemType<any> = {
|
||||
useChildren() {
|
||||
const collection = useCollection();
|
||||
const associatedFields = collection.fields;
|
||||
const cm = useCollectionManager();
|
||||
const isMobield = useIsMobile();
|
||||
const itemComponent = isMobield ? 'TabSearchCollapsibleInputMItem' : 'TabSearchCollapsibleInputItem';
|
||||
const children = associatedFields
|
||||
.map((field) => {
|
||||
if (canBeSearchField(field.interface) || (canBeOptionalField(field.interface) && !field['isForeignKey'])) {
|
||||
const label = field.name;
|
||||
return createTabSearchItemSchema({ field, itemComponent, label });
|
||||
if (
|
||||
!field['isForeignKey'] &&
|
||||
(canBeSearchField(field.interface) || canBeRelatedField(field.interface) || canBeDataField(field.interface))
|
||||
) {
|
||||
const label = canBeRelatedField(field.interface)
|
||||
? cm.getCollection(`${collection.name}.${field.name}`).titleField
|
||||
: field.name;
|
||||
return createTabSearchItemSchema({ field, itemComponent, label, collection });
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
@ -72,8 +72,12 @@ export const TabSearchItemFieldSettings = new SchemaSettings({
|
||||
const { t } = useTranslation();
|
||||
const cm = useCollectionManager();
|
||||
const c = useCollection();
|
||||
const fieldCollection = fieldSchema['x-component-props']?.['collectionName'];
|
||||
const correlation = fieldSchema['x-component-props']?.['correlation'];
|
||||
const collectionField =
|
||||
c.getField(fieldSchema['name']) || cm.getCollectionField(fieldSchema['x-collection-field']);
|
||||
c.getField(fieldSchema['name']) ||
|
||||
cm.getCollectionField(fieldSchema['x-collection-field']) ||
|
||||
cm.getCollection(fieldCollection + '.' + correlation);
|
||||
const compile = useCompile();
|
||||
const { dn } = useDesignable();
|
||||
const targetFields = collectionField?.target ? cm.getCollectionFields(collectionField?.target) : [];
|
||||
@ -87,6 +91,7 @@ export const TabSearchItemFieldSettings = new SchemaSettings({
|
||||
const schema = {
|
||||
['x-uid']: fieldSchema['x-uid'],
|
||||
};
|
||||
|
||||
const fieldNames = {
|
||||
...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'],
|
||||
...fieldSchema['x-component-props']?.['fieldNames'],
|
||||
@ -94,6 +99,7 @@ export const TabSearchItemFieldSettings = new SchemaSettings({
|
||||
};
|
||||
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
|
||||
fieldSchema['x-component-props']['fieldNames'] = fieldNames;
|
||||
|
||||
schema['x-component-props'] = fieldSchema['x-component-props'];
|
||||
dn.emit('patch', {
|
||||
schema,
|
||||
@ -111,7 +117,10 @@ export const TabSearchItemFieldSettings = new SchemaSettings({
|
||||
},
|
||||
useVisible() {
|
||||
const fieldSchema = useFieldSchema();
|
||||
return !isTabSearchCollapsibleInputItem(fieldSchema['x-component']);
|
||||
return (
|
||||
isTabSearchCollapsibleInputItem(fieldSchema['x-component']) ||
|
||||
fieldSchema['x-component-props']?.['correlation']
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { dayjs } from '@nocobase/utils/client';
|
||||
|
||||
const canBeOptionalFields = ['select', 'multipleSelect', 'radioGroup', 'checkboxGroup'];
|
||||
const canBeRelatedFields = ['oho', 'obo', 'o2m', 'm2o', 'm2m'];
|
||||
const canBeDataFields = ['datetime'];
|
||||
|
||||
const canBeSearchFields = [
|
||||
'input',
|
||||
@ -22,5 +25,9 @@ export const isTabSearchCollapsibleInputItem = (component: string) =>
|
||||
|
||||
export const canBeOptionalField = (_interface: string) => canBeOptionalFields.includes(_interface);
|
||||
export const canBeRelatedField = (_interface: string) => canBeRelatedFields.includes(_interface);
|
||||
|
||||
export const canBeDataField = (_interface: string) => canBeDataFields.includes(_interface);
|
||||
export const canBeSearchField = (_interface: string) => canBeSearchFields.includes(_interface);
|
||||
|
||||
export const convertFormat = (currentDate) => {
|
||||
return dayjs(currentDate).format('YYYY-MM-DD');
|
||||
};
|
||||
|
14
packages/plugins/@hera/plugin-mobile/src/locale/en-US.json
Normal file
14
packages/plugins/@hera/plugin-mobile/src/locale/en-US.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"Choices fields":"Choices fields",
|
||||
"Text fields":"Text fields",
|
||||
"Generic properties":"Generic properties",
|
||||
"Search":"Search",
|
||||
"Please enter search content":"Please enter search content",
|
||||
"Title field":"Title field",
|
||||
"Set show field":"Set show field",
|
||||
"Set field count":"Set field count",
|
||||
"Configure field": "Configure field",
|
||||
"Configure fields": "Configure fields",
|
||||
"Configure columns": "Configure columns",
|
||||
"Add block": "Add block"
|
||||
}
|
14
packages/plugins/@hera/plugin-mobile/src/locale/zh-CN.json
Normal file
14
packages/plugins/@hera/plugin-mobile/src/locale/zh-CN.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"Choices fields":"选择字段",
|
||||
"Text fields":"文本字段",
|
||||
"Generic properties":"通用属性",
|
||||
"Search":"搜索",
|
||||
"Please enter search content":"请输入查询内容",
|
||||
"Title field":"标题字段",
|
||||
"Set show field":"设置展示字段",
|
||||
"Set field count":"设置展示数量",
|
||||
"Configure field": "配置字段",
|
||||
"Configure fields": "配置字段",
|
||||
"Configure columns": "配置字段",
|
||||
"Add block": "创建区块"
|
||||
}
|
Loading…
Reference in New Issue
Block a user