feat: 支持轮播图设置和跳转

Reviewed-on: daoyoucloud/tachycode#691
This commit is contained in:
sealday 2024-04-12 15:04:09 +08:00
parent dbdb1135bc
commit 65693d0359
13 changed files with 2486 additions and 3287 deletions

View File

@ -2,6 +2,7 @@ import { Plugin } from '@nocobase/client';
import PluginBlock from './schma-block'; import PluginBlock from './schma-block';
import PluginTabSearch from './schma-component/tab-search'; import PluginTabSearch from './schma-component/tab-search';
import './assets/svg/index'; import './assets/svg/index';
import PluginSwiper from './schma-component/swiper';
class PluginMobileClient extends Plugin { class PluginMobileClient extends Plugin {
async beforeLoad() {} async beforeLoad() {}
@ -11,6 +12,7 @@ class PluginMobileClient extends Plugin {
async afterAdd() { async afterAdd() {
this.pm.add(PluginBlock); this.pm.add(PluginBlock);
this.pm.add(PluginTabSearch); this.pm.add(PluginTabSearch);
this.pm.add(PluginSwiper);
} }
} }

View File

@ -1,21 +1,11 @@
import { Plugin } from '@nocobase/client'; import { Plugin } from '@nocobase/client';
import { NoticeBlock, NoticeBlockInitializer } from './schema-initializer/NoticeBlockInitializer'; import { NoticeBlock, NoticeBlockInitializer } from './schema-initializer/NoticeBlockInitializer';
import { SwiperBlock, SwiperBlockInitializer } from './schema-initializer/SwiperBlockInitializer';
class PluginBlock extends Plugin { class PluginBlock extends Plugin {
async load() { async load() {
this.app.addComponents({ this.app.addComponents({
NoticeBlock, NoticeBlock,
NoticeBlockInitializer, NoticeBlockInitializer,
SwiperBlock,
SwiperBlockInitializer,
});
this.app.schemaInitializerManager.addItem('MBlockInitializers', 'dataBlocks.swiper', {
title: 'swiper',
name: 'swiper',
type: 'item',
Component: 'SwiperBlockInitializer',
}); });
this.app.schemaInitializerManager.addItem('MBlockInitializers', 'dataBlocks.notice', { this.app.schemaInitializerManager.addItem('MBlockInitializers', 'dataBlocks.notice', {
title: 'notice', title: 'notice',

View File

@ -12,6 +12,8 @@ export const createGridCardBlockSchema = (options, componentName) => {
template, template,
dataSource, dataSource,
settings, settings,
value,
childrenSettings,
...others ...others
} = options; } = options;
const resourceName = resource || association || collection; const resourceName = resource || association || collection;
@ -42,6 +44,13 @@ export const createGridCardBlockSchema = (options, componentName) => {
[uid()]: { [uid()]: {
type: 'void', type: 'void',
'x-component': componentName, 'x-component': componentName,
'x-settings': childrenSettings,
'x-component-props': {
resourceName,
fieldName: {
value,
},
},
}, },
}, },
}; };

View File

@ -1,71 +0,0 @@
import {
DataBlockInitializer,
Icon,
css,
useCollectionManager,
useSchemaInitializer,
useSchemaInitializerItem,
} from '@nocobase/client';
import React from 'react';
import { Swiper, Toast } from 'antd-mobile';
import { createGridCardBlockSchema } from '../schema-create/createGridCardBlockSchma';
import { SwiperIcon } from '../../assets/svg';
export const SwiperBlock = () => {
const colors = ['#ace0ff', '#bcffbd', '#e4fabd', '#ffcfac'];
const items = colors.map((color, index) => (
<Swiper.Item key={index}>
<div
className={css`
height: 120px;
color: #ffffff;
display: flex;
justify-content: center;
align-items: center;
font-size: 48px;
user-select: none;
`}
style={{ background: color }}
onClick={() => {
Toast.show(`你点击了卡片 ${index + 1}`);
}}
>
{index + 1}
</div>
</Swiper.Item>
));
return (
<Swiper loop autoplay>
{items}
</Swiper>
);
};
export const SwiperBlockInitializer = () => {
const { insert } = useSchemaInitializer();
const cm = useCollectionManager();
const itemConfig = useSchemaInitializerItem();
const onCreateBlockSchema = async ({ item }) => {
const collection = cm.getCollection(item.name);
const schema = createGridCardBlockSchema(
{
collection: item.name,
dataSource: item.dataSource,
rowKey: collection.filterTargetKey || 'id',
settings: 'blockSettings:gridCard',
},
'SwiperBlock',
);
insert(schema);
};
return (
<DataBlockInitializer
{...itemConfig}
icon={<Icon type="swiper-block" />}
componentType={'Swiper'}
onCreateBlockSchema={onCreateBlockSchema}
/>
);
};

View File

@ -0,0 +1,44 @@
import { BlockItem, css, useDesignable, useRequest, withDynamicSchemaProps } from '@nocobase/client';
import { useFieldSchema } from '@nocobase/schema';
import { Swiper } from 'antd-mobile';
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
export const SwiperBlock = withDynamicSchemaProps((props) => {
const { fieldValue, data, changeNav } = props;
if (!data) return;
const items = data['data']
?.map((imgItem, index) => {
return imgItem[fieldValue] ? (
<Swiper.Item key={index}>
<div
className={css`
height: 20vh;
color: #ffffff;
display: flex;
justify-content: center;
align-items: center;
font-size: 48px;
user-select: none;
`}
style={{
backgroundImage: `url(${imgItem[fieldValue][0].url})`,
backgroundSize: '100% 100%',
backgroundAttachment: 'fixed',
}}
onClick={() => {
changeNav(imgItem);
}}
></div>
</Swiper.Item>
) : null;
})
.filter(Boolean);
return (
<BlockItem>
<Swiper loop autoplay>
{items}
</Swiper>
</BlockItem>
);
});

View File

@ -0,0 +1,50 @@
import {
DataBlockInitializer,
Icon,
useCollectionManager,
useSchemaInitializer,
useSchemaInitializerItem,
} from '@nocobase/client';
import React from 'react';
import { createGridCardBlockSchema } from '../../schma-block/schema-create/createGridCardBlockSchma';
import { ISchema } from '@nocobase/schema';
export const SwiperBlockInitializer = () => {
const { insert } = useSchemaInitializer();
const cm = useCollectionManager();
const itemConfig = useSchemaInitializerItem();
const onCreateBlockSchema = async ({ item }) => {
const collection = cm.getCollection(item.name);
const value = collection.fields.find((field) => field.interface === 'attachment');
if (!value) return;
const schema: ISchema = {
type: 'void',
name: item.name,
title: collection.title,
'x-decorator': 'DataBlockProvider',
'x-decorator-props': {
collection: item.name,
dataSource: 'main',
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'SwiperFieldSettings',
'x-component': 'SwiperBlock',
'x-use-component-props': 'useSwiperBlockProps',
'x-component-props': {
resourceName: item.name,
fieldValue: value.name,
},
};
insert(schema);
};
return (
<DataBlockInitializer
{...itemConfig}
icon="swiper-block"
componentType={'Swiper'}
onCreateBlockSchema={onCreateBlockSchema}
/>
);
};

View File

@ -0,0 +1,124 @@
import {
SchemaSettings,
SchemaSettingsDataScope,
useCollection,
useCompile,
useDesignable,
useFormBlockContext,
} from '@nocobase/client';
import { useField, useFieldSchema } from '@nocobase/schema';
import _ from 'lodash';
import { useTranslation } from '../../locale';
export const SwiperFieldSettings = new SchemaSettings({
name: 'SwiperFieldSettings',
items: [
{
name: 'setTheDataScope',
Component: SchemaSettingsDataScope,
useComponentProps() {
const fieldSchema = useFieldSchema();
const { form } = useFormBlockContext();
const field = useField();
const c = useCollection();
const { dn } = useDesignable();
return {
collectionName: c.getOptions().name,
defaultFilter: fieldSchema?.['x-component-props']?.params?.filter || {},
form: form,
onSubmit: ({ filter }) => {
_.set(field.componentProps, 'params', {
...field.componentProps?.params,
filter,
});
fieldSchema['x-component-props']['params'] = field.componentProps.params;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-component-props': fieldSchema['x-component-props'],
},
});
},
};
},
},
{
name: 'setShowField',
type: 'select',
useComponentProps() {
const fieldSchema = useFieldSchema();
const { t } = useTranslation();
const c = useCollection();
const compile = useCompile();
const { dn } = useDesignable();
const options = c.fields
.map((field) => {
if (field?.interface === 'attachment') {
return {
label: compile(field.uiSchema?.title) || field.name,
value: field.name,
};
}
})
.filter(Boolean);
const onChange = (label) => {
const schema = {
['x-uid']: fieldSchema['x-uid'],
};
fieldSchema['x-component-props']['fieldValue'] = label;
schema['x-component-props'] = fieldSchema['x-component-props'];
dn.emit('patch', {
schema,
});
dn.refresh();
};
return {
title: t('SetShowfield'),
options,
value: fieldSchema['x-component-props'].fieldValue,
onChange,
};
},
},
{
name: 'setFieldCount',
type: 'select',
useComponentProps() {
const fieldSchema = useFieldSchema();
const { dn } = useDesignable();
const options = [];
for (let i = 1; i <= 20; i++) {
options.push({
label: i,
value: i,
});
}
const onChange = (value) => {
fieldSchema['x-component-props']['pageSize'] = value;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
['x-component-props']: fieldSchema['x-component-props'],
},
});
dn.refresh();
};
return {
title: 'setFieldCount',
options,
value: fieldSchema['x-component-props'].pageSize || 5,
onChange,
};
},
},
{
name: 'divider',
type: 'divider',
},
{
name: 'delete',
type: 'remove',
sort: 100,
},
],
});

View File

@ -0,0 +1,22 @@
import {
CollectionRecordProvider,
DataBlockProvider,
RemoteSchemaComponent,
useCollectionRecord,
} from '@nocobase/client';
import React from 'react';
import { useParams } from 'react-router-dom';
export const SwiperPage = () => {
const params = useParams();
const { name, collection, field, fieldParams } = params;
const record = {};
record[field] = fieldParams;
return (
<DataBlockProvider collection={collection} dataSource="main">
<CollectionRecordProvider record={record} parentRecord={null}>
<RemoteSchemaComponent uid={name} />
</CollectionRecordProvider>
</DataBlockProvider>
);
};

View File

@ -0,0 +1,33 @@
import { Plugin } from '@nocobase/client';
import { SwiperBlockInitializer } from './SwiperBlockInitializer';
import { SwiperBlock } from './SwiperBlock';
import { SwiperFieldSettings } from './SwiperFieldSettings';
import { useSwiperBlockProps } from './useSwiperBlockProps';
import { SwiperPage } from './SwiperPage';
class PluginSwiper extends Plugin {
async load() {
this.app.addScopes({
useSwiperBlockProps,
});
this.app.addComponents({
SwiperBlockInitializer,
SwiperBlock,
SwiperPage,
});
this.app.schemaInitializerManager.addItem('mobilePage:addBlock', 'dataBlocks.swiper', {
title: 'swiper',
name: 'swiper',
type: 'item',
Component: 'SwiperBlockInitializer',
});
this.app.schemaSettingsManager.add(SwiperFieldSettings);
this.app.router.add('mobile.swiper.page', {
path: '/mobile/:name/image/:collection/:field/:fieldParams',
Component: 'SwiperPage',
});
}
}
export default PluginSwiper;

View File

@ -0,0 +1,86 @@
import { useFieldSchema } from '@nocobase/schema';
import { useNavigate } from 'react-router-dom';
import React, { useEffect } from 'react';
import { useCollection, useDesignable, useRequest } from '@nocobase/client';
export const useSwiperBlockProps = () => {
const fieldSchema = useFieldSchema();
const navigate = useNavigate();
const properties = fieldSchema.properties ? Object.values(fieldSchema.properties) : null;
const { insertAdjacent } = useDesignable();
const collection = useCollection();
const field = collection.targetKey || 'id';
const { filter, pageSize, fieldValue, resourceName } = fieldSchema['x-component-props'];
const { data, run } = useRequest({
resource: resourceName + ':list',
params: {
filter,
pageSize: pageSize || 5,
appends: fieldValue ? [fieldValue] : [],
},
});
useEffect(() => {
run();
}, [filter, pageSize, fieldValue]);
const changeNav = (imgItem) => {
if (imgItem['isIdentical']) {
const existence = properties?.find((value) => value['x-component-props']['fixedComponent']);
if (existence) {
navigate(`/mobile/${existence['x-uid']}/image/${resourceName}/${field}/${imgItem[field]}`);
} else {
insertAdjacent('beforeEnd', swiperCustomPage(imgItem, 'fixedComponent'), {
onSuccess(res) {
if (res) {
navigate(`/mobile/${res['x-uid']}`);
}
},
});
}
} else {
const existence = properties?.find((value) => value.name === String(imgItem.id));
if (existence) {
navigate(`/mobile/${existence['x-uid']}`);
} else {
insertAdjacent('beforeEnd', swiperCustomPage(imgItem), {
onSuccess(res) {
if (res) {
navigate(`/mobile/${res['x-uid']}`);
}
},
});
}
}
};
return {
data,
fieldValue,
changeNav,
};
};
const swiperCustomPage = (imgItem, fixedComponent?) => {
return {
type: 'void',
title: imgItem.title,
name: imgItem.id,
'x-component': 'MMenu.Item',
'x-component-props': { imgItem, fixedComponent },
'x-designer': 'MMenu.Item.Designer',
properties: {
page: {
type: 'void',
'x-component': 'MPage',
'x-designer': 'MPage.Designer',
'x-component-props': {},
properties: {
grid: {
type: 'void',
'x-component': 'Grid',
'x-initializer': 'mobilePage:addBlock',
},
},
},
},
};
};

View File

@ -16,7 +16,7 @@ const textFieldsItem: SchemaInitializerItemType<any> = {
const itemComponent = isMobield ? 'TabSearchCollapsibleInputMItem' : 'TabSearchCollapsibleInputItem'; const itemComponent = isMobield ? 'TabSearchCollapsibleInputMItem' : 'TabSearchCollapsibleInputItem';
const children = associatedFields const children = associatedFields
.map((field) => { .map((field) => {
if (canBeSearchField(field.interface) && !field['isForeignKey']) { if (canBeSearchField(field.interface) || (canBeOptionalField(field.interface) && !field['isForeignKey'])) {
const label = field.name; const label = field.name;
return createTabSearchItemSchema({ field, itemComponent, label }); return createTabSearchItemSchema({ field, itemComponent, label });
} }

View File

@ -14,6 +14,7 @@ const canBeSearchFields = [
'color', 'color',
'icon', 'icon',
'formula', 'formula',
'sequence',
]; ];
export const isTabSearchCollapsibleInputItem = (component: string) => export const isTabSearchCollapsibleInputItem = (component: string) =>

File diff suppressed because it is too large Load Diff