diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/fieldSettingsFormItem.tsx b/packages/core/client/src/modules/blocks/data-blocks/form/fieldSettingsFormItem.tsx index 32e269f8a..d951d0a31 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/fieldSettingsFormItem.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/form/fieldSettingsFormItem.tsx @@ -15,6 +15,7 @@ import { isPatternDisabled } from '../../../../schema-settings'; import { ActionType } from '../../../../schema-settings/LinkageRules/type'; import { SchemaSettingsDefaultValue } from '../../../../schema-settings/SchemaSettingsDefaultValue'; import { useIsAllowToSetDefaultValue } from '../../../../schema-settings/hooks/useIsAllowToSetDefaultValue'; +import { css } from '@nocobase/client'; export const fieldSettingsFormItem = new SchemaSettings({ name: 'fieldSettings:FormItem', @@ -223,6 +224,47 @@ export const fieldSettingsFormItem = new SchemaSettings({ }, Component: SchemaSettingsDefaultValue, }, + { + name: 'layoutDirection', + type: 'select', + useComponentProps() { + const { t } = useTranslation(); + const fieldSchema = useFieldSchema(); + const { dn } = useDesignable(); + const initialValue = fieldSchema['x-decorator-props']?.layoutDirection ?? 'column'; + return { + title: t('Layout Direction'), + options: [ + { label: t('Row'), value: 'row' }, + { label: t('Column'), value: 'column' }, + ], + value: initialValue, + onChange(v) { + const schema: ISchema = { + ['x-uid']: fieldSchema['x-uid'], + }; + + const styleValue = { + layoutDirection: v ?? 'column', + style: { + display: 'flex', + flexDirection: `${v === 'row' ? 'row' : 'column'}`, + alignItems: 'baseline', + }, + }; + + _.set(fieldSchema, 'x-decorator-props', styleValue); + _.set(schema, 'x-decorator-props', styleValue); + + dn.emit('patch', { + schema, + }); + + dn.refresh(); + }, + }; + }, + }, { name: 'pattern', type: 'select', diff --git a/packages/core/client/src/schema-component/antd/grid-card/infinite-scroll/InfiniteScroll.tsx b/packages/core/client/src/schema-component/antd/grid-card/infinite-scroll/InfiniteScroll.tsx new file mode 100644 index 000000000..d86c57af4 --- /dev/null +++ b/packages/core/client/src/schema-component/antd/grid-card/infinite-scroll/InfiniteScroll.tsx @@ -0,0 +1,173 @@ +import { useLockFn, useThrottleFn } from 'ahooks'; +import type { FC, ReactNode } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; +// import { useConfig } from '../config-provider'; +// import DotLoading from '../dot-loading'; +import { NativeProps, getScrollParent, mergeProps, withNativeProps } from './utils'; + +// TODO: 待继续 +const useConfig = () => { + return { + locale: { + InfiniteScroll: { + retry: false, + noMore: false, + failedToLoad: '范德萨发', + }, + common: { + loading: false, + }, + }, + }; +}; + +// TODO: 待继续 +const DotLoading = () => null; + +function isWindow(element: any | Window): element is Window { + return element === window; +} + +const classPrefix = `adm-infinite-scroll`; + +export type InfiniteScrollProps = { + loadMore: (isRetry: boolean) => Promise; + hasMore: boolean; + threshold?: number; + children?: ReactNode | ((hasMore: boolean, failed: boolean, retry: () => void) => ReactNode); +} & NativeProps; + +const defaultProps: Required> = { + threshold: 250, + children: (hasMore: boolean, failed: boolean, retry: () => void) => ( + + ), +}; + +export const InfiniteScroll: FC = (p) => { + const props = mergeProps(defaultProps, p); + + const [failed, setFailed] = useState(false); + const doLoadMore = useLockFn(async (isRetry: boolean) => { + try { + await props.loadMore(isRetry); + } catch (e) { + setFailed(true); + throw e; + } + }); + + const elementRef = useRef(null); + + // Prevent duplicated trigger of `check` function + const [flag, setFlag] = useState({}); + const nextFlagRef = useRef(flag); + + const [scrollParent, setScrollParent] = useState(); + + const { run: check } = useThrottleFn( + async () => { + if (nextFlagRef.current !== flag) return; + if (!props.hasMore) return; + const element = elementRef.current; + if (!element) return; + if (!element.offsetParent) return; + const parent = getScrollParent(element); + setScrollParent(parent); + if (!parent) return; + const rect = element.getBoundingClientRect(); + const elementTop = rect.top; + const current = isWindow(parent) ? window.innerHeight : parent.getBoundingClientRect().bottom; + if (current >= elementTop - props.threshold) { + const nextFlag = {}; + nextFlagRef.current = nextFlag; + try { + await doLoadMore(false); + setFlag(nextFlag); + } catch (e) { + console.log('%c Line:88 🍯 e', 'font-size:18px;color:#2eafb0;background:#3f7cff', e); + } + } + }, + { + wait: 100, + leading: true, + trailing: true, + }, + ); + + // Make sure to trigger `loadMore` when content changes + useEffect(() => { + check(); + }); + + useEffect(() => { + const element = elementRef.current; + if (!element) return; + if (!scrollParent) return; + function onScroll() { + check(); + } + scrollParent.addEventListener('scroll', onScroll); + return () => { + scrollParent.removeEventListener('scroll', onScroll); + }; + }, [scrollParent]); + + async function retry() { + setFailed(false); + try { + await doLoadMore(true); + setFlag(nextFlagRef.current); + } catch (e) { + console.log('%c Line:121 🥥 e', 'font-size:18px;color:#3f7cff;background:#2eafb0', e); + } + } + + return withNativeProps( + props, +
+ {typeof props.children === 'function' ? props.children(props.hasMore, failed, retry) : props.children} +
, + ); +}; + +const InfiniteScrollContent: FC<{ + hasMore: boolean; + failed: boolean; + retry: () => void; +}> = (props) => { + const { locale } = useConfig(); + + if (!props.hasMore) { + return {locale.InfiniteScroll.noMore}; + } + + if (props.failed) { + return ( + + {locale.InfiniteScroll.failedToLoad} + { + props.retry(); + }} + > + {locale.InfiniteScroll.retry} + + + ); + } + + return ( + <> + {locale.common.loading} + + + ); +}; + +const keepComp = (props) => { + return ; +}; + +keepComp({}); diff --git a/packages/core/client/src/schema-component/antd/grid-card/infinite-scroll/config-prodider.tsx b/packages/core/client/src/schema-component/antd/grid-card/infinite-scroll/config-prodider.tsx new file mode 100644 index 000000000..75b58ef9e --- /dev/null +++ b/packages/core/client/src/schema-component/antd/grid-card/infinite-scroll/config-prodider.tsx @@ -0,0 +1,54 @@ +import React, { useContext } from 'react'; +import type { FC, ReactNode } from 'react'; + +// TODO: 待继续 +// import { Locale } from '../../locales/base'; +// import zhCN from '../../locales/zh-CN'; +type Locale = {}; +const zhCN = {}; + +type Config = { + locale: Locale; +}; + +export const defaultConfigRef: { + current: Config; +} = { + current: { + locale: zhCN, + }, +}; + +export function setDefaultConfig(config: Config) { + defaultConfigRef.current = config; +} + +export function getDefaultConfig() { + return defaultConfigRef.current; +} + +const ConfigContext = React.createContext(null); + +export type ConfigProviderProps = Config & { + children?: ReactNode; +}; + +export const ConfigProvider: FC = (props) => { + const { children, ...config } = props; + const parentConfig = useConfig(); + + return ( + + {children} + + ); +}; + +export function useConfig() { + return useContext(ConfigContext) ?? getDefaultConfig(); +} diff --git a/packages/core/client/src/schema-component/antd/grid-card/infinite-scroll/locale.ts b/packages/core/client/src/schema-component/antd/grid-card/infinite-scroll/locale.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/core/client/src/schema-component/antd/grid-card/infinite-scroll/utils.ts b/packages/core/client/src/schema-component/antd/grid-card/infinite-scroll/utils.ts new file mode 100644 index 000000000..76b84910e --- /dev/null +++ b/packages/core/client/src/schema-component/antd/grid-card/infinite-scroll/utils.ts @@ -0,0 +1,85 @@ +import classNames from 'classnames'; +import type { CSSProperties, ReactElement } from 'react'; +import React, { AriaAttributes } from 'react'; + +const canUseDom = !!( + typeof window !== 'undefined' && + typeof document !== 'undefined' && + window.document && + window.document.createElement +); + +type ScrollElement = HTMLElement | Window; + +const defaultRoot = canUseDom ? window : undefined; + +const overflowStylePatterns = ['scroll', 'auto', 'overlay']; + +function isElement(node: Element) { + const ELEMENT_NODE_TYPE = 1; + return node.nodeType === ELEMENT_NODE_TYPE; +} +export function getScrollParent( + el: Element, + root: ScrollElement | null | undefined = defaultRoot, +): Window | Element | null | undefined { + let node = el; + + while (node && node !== root && isElement(node)) { + if (node === document.body) { + return root; + } + const { overflowY } = window.getComputedStyle(node); + if (overflowStylePatterns.includes(overflowY) && node.scrollHeight > node.clientHeight) { + return node; + } + node = node.parentNode as Element; + } + return root; +} + +export function mergeProps(a: A, b: B): B & A; +export function mergeProps(a: A, b: B, c: C): C & B & A; +export function mergeProps(...items: any[]) { + const ret: any = {}; + items.forEach((item) => { + Object.keys(item).forEach((key) => { + if (item[key] !== undefined) { + ret[key] = item[key]; + } + }); + }); + return ret; +} + +export type NativeProps = { + className?: string; + style?: CSSProperties & Partial>; + tabIndex?: number; +} & AriaAttributes; + +export function withNativeProps

(props: P, element: ReactElement) { + const p = { + ...element.props, + }; + if (props.className) { + p.className = classNames(element.props.className, props.className); + } + if (props.style) { + p.style = { + ...p.style, + ...props.style, + }; + } + if (props.tabIndex !== undefined) { + p.tabIndex = props.tabIndex; + } + for (const key in props) { + // if (!props.hasOwnProperty(key)) continue; + if (!Object.hasOwn(props, key)) continue; + if (key.startsWith('data-') || key.startsWith('aria-')) { + p[key] = props[key]; + } + } + return React.cloneElement(element, p); +} diff --git a/packages/core/client/src/schema-settings/SchemaSettings.tsx b/packages/core/client/src/schema-settings/SchemaSettings.tsx index 2dcc02d2a..af491608b 100644 --- a/packages/core/client/src/schema-settings/SchemaSettings.tsx +++ b/packages/core/client/src/schema-settings/SchemaSettings.tsx @@ -620,17 +620,33 @@ export const SchemaSettingsConnectDataBlocks: FC { const title = `${compile(block.collection.title)} #${block.uid.slice(0, 4)}`; const onHover = () => { + // 保存当前的滚动位置 + const originalScrollPosition = window.scrollY || window.pageYOffset || document.documentElement.scrollTop; + const dom = block.dom; const designer = dom.querySelector('.general-schema-designer') as HTMLElement; if (designer) { designer.style.display = 'block'; } dom.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.2)'; - dom.scrollIntoView({ - behavior: 'smooth', - block: 'center', - }); + + setTimeout(() => { + // 使用平滑滚动滚回到原始位置 + dom.scrollIntoView({ + behavior: 'smooth', + block: 'center', + }); + }, 1000); + + setTimeout(() => { + // 使用平滑滚动滚回到原始位置 + window.scrollTo({ + top: originalScrollPosition, + behavior: 'smooth', + }); + }, 2000); }; + const onLeave = () => { const dom = block.dom; const designer = dom.querySelector('.general-schema-designer') as HTMLElement; @@ -662,8 +678,9 @@ export const SchemaSettingsConnectDataBlocks: FC ); } diff --git a/packages/plugins/@hera/plugin-mobile/src/client/common/plugin.ts b/packages/plugins/@hera/plugin-mobile/src/client/common/plugin.ts new file mode 100644 index 000000000..9f2b8e3ca --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/common/plugin.ts @@ -0,0 +1,17 @@ +import { Plugin, tval } from '@nocobase/client'; + +class PluginCommon extends Plugin { + async afterAdd() {} + async beforeLoad() {} + + async load() { + // 扩展原移动端插件的 block 类型 + this.app.schemaInitializerManager.addItem('mobilePage:addBlock', 'filterBlocks', { + title: tval('Filter blocks'), + type: 'itemGroup', + children: [], + }); + } +} + +export default PluginCommon; diff --git a/packages/plugins/@hera/plugin-mobile/src/client/index.tsx b/packages/plugins/@hera/plugin-mobile/src/client/index.tsx index 83b77300d..c349f71ab 100644 --- a/packages/plugins/@hera/plugin-mobile/src/client/index.tsx +++ b/packages/plugins/@hera/plugin-mobile/src/client/index.tsx @@ -1,19 +1,24 @@ import { Plugin } from '@nocobase/client'; +import PluginCommon from './common/plugin'; import PluginBlock from './schma-block'; +import PluginImageSearch from './schma-component/image-search/plugin'; +import PluginSwiper from './schma-component/swiper'; import PluginTabSearch from './schma-component/tab-search'; import './assets/svg/index'; -import PluginSwiper from './schma-component/swiper'; class PluginMobileClient extends Plugin { - async beforeLoad() {} - - async load() {} - async afterAdd() { + this.pm.add(PluginCommon); this.pm.add(PluginBlock); this.pm.add(PluginTabSearch); this.pm.add(PluginSwiper); + this.pm.add(PluginImageSearch); + // this.app.router.add() } + + async beforeLoad() {} + + async load() {} } export default PluginMobileClient; diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.configure.ts b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.configure.ts new file mode 100644 index 000000000..ed6ff233e --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.configure.ts @@ -0,0 +1,53 @@ +import { SchemaInitializer, useCollection, useCollectionManager } from '@nocobase/client'; +import { tval } from '../../locale'; +import { useIsMobile } from '../tab-search/components/field-item/hooks'; +import { canBeOptionalField, canBeRelatedField } from '../tab-search/utils'; +import { createSchemaImageSearchItem } from './search-item/ImageSearchItem.schema'; + +export const ImageSearchConfigureFields = new SchemaInitializer({ + name: 'ImageSearchView:configureFields', + title: tval('Configure fields'), + style: { marginTop: 16 }, + icon: 'SettingOutlined', + items: [ + { + name: 'choicesFields', + type: 'itemGroup', + title: tval('Choices fields'), + useChildren: useChildrenChoicesFieldSchemas, + }, + ], +}); + +function useChildrenChoicesFieldSchemas() { + const collection = useCollection(); + const fields = collection?.fields; + const cm = useCollectionManager(); + const isMobile = useIsMobile(); + + const choicesFieldsSchemas = fields + .map((field) => { + const { interface: _interface, target: collectionName } = field; + const label = cm.getCollection(collectionName)?.getPrimaryKey() ?? 'id'; + const isCanBeOptional = canBeOptionalField(_interface); + const isCanBeRelated = canBeRelatedField(_interface); + + if (isCanBeOptional || isCanBeRelated) { + const schema = createSchemaImageSearchItem({ + collection, + field, + isMobile, + label, + isCanBeOptional, + isCanBeRelated, + }); + + return schema; + } + + return null; + }) + .filter(Boolean); + + return choicesFieldsSchemas; +} diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.initializer.tsx b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.initializer.tsx new file mode 100644 index 000000000..256d925da --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.initializer.tsx @@ -0,0 +1,37 @@ +import { DataBlockInitializer, useSchemaInitializer, useSchemaInitializerItem } from '@nocobase/client'; +import React from 'react'; +import { createSchemaImageSearchBlock } from './ImageSearch.schema'; + +interface ItemConfig { + name: string; + title: string; + icon: string; +} + +export function ImageSearchInitializer(props) { + const { filterCollections } = props; + const itemConfig: ItemConfig = useSchemaInitializerItem(); + + const { insert } = useSchemaInitializer(); + + const onCreateBlockSchema = async ({ item }) => { + const { dataSource, collectionName, name } = item; + + const schema = createSchemaImageSearchBlock({ + dataSource, + collection: collectionName || name, + blockType: 'filter', + }); + + insert(schema); + }; + + return ( + + ); +} diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.provider.tsx b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.provider.tsx new file mode 100644 index 000000000..10a9a0f83 --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.provider.tsx @@ -0,0 +1,6 @@ +import { DataBlockProvider } from '@nocobase/client'; +import React from 'react'; + +export const ImageSearchProvider = ({ collection, children }) => { + return {children}; +}; diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.schema.ts b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.schema.ts new file mode 100644 index 000000000..8ae5dd1f4 --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.schema.ts @@ -0,0 +1,34 @@ +import { ISchema } from '@tachybase/schema'; +import { uid } from '@nocobase/utils/client'; + +interface OptionsType { + collection: string; + dataSource: string; + blockType: string; +} + +export function createSchemaImageSearchBlock(options: OptionsType): ISchema { + const { collection, dataSource, blockType } = options; + return { + type: 'void', + // TODO: 困惑, 这里不能直接用 DataBlockProvider, 因为它没有collection和dataSource属性 + 'x-decorator': 'ImageSearchProvider', + 'x-decorator-props': { + collection, + dataSource, + blockType, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:filterCollapse', + 'x-component': 'CardItem', + 'x-filter-targets': [], + properties: { + [uid()]: { + type: 'void', + 'x-action': 'imageSearch', + 'x-initializer': 'ImageSearchView:configureFields', + 'x-component': 'ImageSearchView', + }, + }, + }; +} diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.view.tsx b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.view.tsx new file mode 100644 index 000000000..7d2ee09eb --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/ImageSearch.view.tsx @@ -0,0 +1,19 @@ +import { useDesigner, useSchemaInitializerRender } from '@nocobase/client'; +import { RecursionField, useFieldSchema } from '@tachybase/schema'; +import React from 'react'; + +export const ImageSearchView = () => { + const Designer = useDesigner(); + const fieldSchema = useFieldSchema(); + const { render } = useSchemaInitializerRender(fieldSchema['x-initializer'], fieldSchema['x-initializer-props']); + + return ( + <> + + + {render()} + + ); +}; + +export default ImageSearchView; diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/data.ts b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/data.ts new file mode 100644 index 000000000..8512ffbf6 --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/data.ts @@ -0,0 +1,22 @@ +export const images = [ + { + label: '全部商品', + imageUrl: 'https://courier.dzkbdd.cn/static/jx-h5/assets/operator_all_select-884b60ce.png', + }, + { + label: '中国电信', + imageUrl: 'https://courier.dzkbdd.cn/static/jx-h5/assets/operator_dx-7df8411f.png', + }, + { + label: '中国移动', + imageUrl: 'https://courier.dzkbdd.cn/static/jx-h5/assets/operator_yd-7b994595.png', + }, + { + label: '中国联通', + imageUrl: 'https://courier.dzkbdd.cn/static/jx-h5/assets/operator_lt-d9888233.png', + }, + { + label: '中国广电', + imageUrl: 'https://courier.dzkbdd.cn/static/jx-h5/assets/operator_gd-cc48f3e6.png', + }, +]; diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/hooks/useAction.ImageSearchItemView.ts b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/hooks/useAction.ImageSearchItemView.ts new file mode 100644 index 000000000..9cb34a4e3 --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/hooks/useAction.ImageSearchItemView.ts @@ -0,0 +1,61 @@ +import { useCollection, useDesigner } from '@nocobase/client'; +import { useFieldSchema } from '@tachybase/schema'; +import React from 'react'; +import { useTranslation } from '../../../locale'; + +export function useActionImageSearchItemView(props) { + const { list, onSelected, valueKey: _valueKey, labelKey: _labelKey, filterKey } = props; + const fieldSchema = useFieldSchema(); + const collection = useCollection(); + const { t } = useTranslation(); + const collectionField = React.useMemo( + () => collection?.getField(fieldSchema['fieldName'] as any), + [collection, fieldSchema['fieldName']], + ); + const Designer = useDesigner(); + const valueKey = _valueKey || collectionField?.targetKey || 'id'; + const labelKey = _labelKey || fieldSchema['x-component-props']?.fieldNames?.label || valueKey; + + const onSelect = (itemKey) => { + const key = itemKey.keyPath?.[0] || itemKey; + onSelected([key], filterKey); + }; + + const fieldNames = { + title: labelKey || valueKey, + key: valueKey, + }; + + const itemsData = (list || []).map((item) => { + const { type, ['image_show']: imageObj, [fieldNames.title]: label, [fieldNames.key]: key } = item; + + // TODO: 需要更好的处理url方式 + const origin = location?.origin || ''; + const sourceUrl = imageObj?.[0]?.url; + let imageUrl = sourceUrl ?? ''; + if (!imageUrl.includes('http')) { + imageUrl = `${origin}${sourceUrl}`; + } + + if (type === 'all') { + return { + label: t('AllProducts'), + key: 'all', + imageUrl: imageUrl, + }; + } + + return { + label, + key, + imageUrl: imageUrl, + }; + }); + + return { + collectionField, + Designer, + items: itemsData, + onSelect, + }; +} diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/hooks/useSelect.ts b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/hooks/useSelect.ts new file mode 100644 index 000000000..878aaedaa --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/hooks/useSelect.ts @@ -0,0 +1,73 @@ +import { findFilterTargets, mergeFilter, useCollection, useFilterBlock } from '@nocobase/client'; +import { useFieldSchema } from '@tachybase/schema'; +import _ from 'lodash'; +import { useMemo } from 'react'; + +export const useGetSelected = () => { + const fieldSchema = useFieldSchema(); + const collection = useCollection(); + const { getDataBlocks } = useFilterBlock(); + const collectionField = useMemo(() => collection?.getField(fieldSchema.name as any), [collection, fieldSchema.name]); + + 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] || {}; + // 保留原有的 filter + let storedFilter = block.service.params?.[1]?.filters || {}; + if (value.length) { + storedFilter[key] = { + [filterKey]: value, + }; + } else { + if (block.dataLoadingMode === 'manual') { + return block.clearData(); + } + delete storedFilter[key]; + } + const mergedFilter = mergeFilter([...Object.values(storedFilter), block.defaultFilter]); + if (value.length === 1 && value[0] === 'all') { + const currentKey = Object.keys(storedFilter[key]); + if (mergedFilter['$and']) { + currentKey.forEach((currentValue) => { + mergedFilter['$and'].forEach((value, index) => { + if (value[currentValue]) { + mergedFilter['$and'].splice(index, 1); + } + }); + }); + mergedFilter['$and'] = mergedFilter['$and'].filter(Boolean); + } else { + delete mergedFilter[currentKey[0]]; + } + storedFilter[key] = {}; + } + storedFilter = _.omitBy( + storedFilter, + (value) => value === null || value === undefined || (_.isObject(value) && _.isEmpty(value)), + ); + + return block.doFilter( + { + ...param, + page: 1, + filter: mergedFilter, + }, + { filters: storedFilter }, + ); + }); + }; + + return { + 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/@hera/plugin-mobile/src/client/schma-component/image-search/plugin.ts b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/plugin.ts new file mode 100644 index 000000000..b7d3a8ec5 --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/plugin.ts @@ -0,0 +1,40 @@ +import { Plugin } from '@nocobase/client'; +import { ImageSearchView } from './ImageSearch.view'; +import { ImageSearchInitializer } from './ImageSearch.initializer'; +import { ImageSearchConfigureFields } from './ImageSearch.configure'; +import { ImageSearchProvider } from './ImageSearch.provider'; +import { ImageSearchItemIntializer } from './search-item/ImageSearchItem.intializer'; +import { ImageSearchItemToolbar } from './search-item/ImageSearchItem.toolbar'; +import { ImageSearchItemView } from './search-item/ImageSearchItem.view'; +import { ImageSearchItemFieldSettings } from './search-item/ImageSearchItem.setting'; +import { usePropsOptionalImageSearchItemField } from './search-item/useProps.Optional'; +import { usePropsRelatedImageSearchItemField } from './search-item/useProps.Related'; + +class PluginImageSearch extends Plugin { + async load() { + this.app.addScopes({ + usePropsOptionalImageSearchItemField: usePropsOptionalImageSearchItemField, + usePropsRelatedImageSearchItemField: usePropsRelatedImageSearchItemField, + }); + this.app.addComponents({ + ImageSearchView: ImageSearchView, + ImageSearchInitializer: ImageSearchInitializer, + ImageSearchProvider: ImageSearchProvider, + ImageSearchItemIntializer: ImageSearchItemIntializer, + ImageSearchItemToolbar: ImageSearchItemToolbar, + ImageSearchItemView: ImageSearchItemView, + }); + + this.app.schemaInitializerManager.add(ImageSearchConfigureFields); + this.schemaSettingsManager.add(ImageSearchItemFieldSettings); + + this.app.schemaInitializerManager.addItem('mobilePage:addBlock', 'filterBlocks.imageSearch', { + name: 'imageSearch', + title: 'imageSearch', + icon: 'tab-search', + Component: 'ImageSearchInitializer', + }); + } +} + +export default PluginImageSearch; diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.intializer.tsx b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.intializer.tsx new file mode 100644 index 000000000..d78dffbce --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.intializer.tsx @@ -0,0 +1,28 @@ +import { + SchemaInitializerSwitch, + useCurrentSchema, + useSchemaInitializer, + useSchemaInitializerItem, +} from '@nocobase/client'; +import { merge } from '@tachybase/schema'; +import React from 'react'; + +export const ImageSearchItemIntializer = () => { + const itemConfig = useSchemaInitializerItem(); + const { insert } = useSchemaInitializer(); + const { schema: oldSchema, title } = itemConfig; + const { exists, remove } = useCurrentSchema(oldSchema.name, 'name', itemConfig.find, itemConfig.remove); + + const onClick = () => { + if (exists) { + return remove(); + } + const { schema: latestSchema } = itemConfig; + + const newSchema = merge(oldSchema || {}, latestSchema || {}); + // itemConfig?.schemaInitialize?.(newSchema); + insert(newSchema); + }; + + return ; +}; diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.schema.ts b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.schema.ts new file mode 100644 index 000000000..6ecdfd648 --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.schema.ts @@ -0,0 +1,45 @@ +import { SchemaInitializerItemType } from '@nocobase/client'; + +interface OptionsType { + label?: string; + field?: any; + collection?: any; + isMobile?: boolean; + isCanBeOptional?: boolean; + isCanBeRelated?: boolean; +} + +export function createSchemaImageSearchItem(options: OptionsType): SchemaInitializerItemType { + const { field, isMobile, label, isCanBeOptional, isCanBeRelated, collection } = options; + const { key, name, uiSchema, interface: _interface } = field; + const title = uiSchema?.title; + const indexOfUseProps = [isCanBeOptional, isCanBeRelated].indexOf(true); + return { + name: key, + title: title, + Component: 'ImageSearchItemIntializer', + schema: { + name: `${name}-choice`, + fieldName: name, + title: title, + type: 'void', + 'x-toolbar': 'ImageSearchItemToolbar', + 'x-settings': 'fieldSettings:component:ImageSearchItem', + 'x-component': 'ImageSearchItemView', + 'x-component-props': { + fieldNames: { + label, + // 固定字段, 用于取数据表对外展示的图片字段 + imageShow: 'image_show', + }, + interface: _interface, + collectionName: collection?.name, + correlation: name, + }, + 'x-use-component-props': ['usePropsOptionalImageSearchItemField', 'usePropsRelatedImageSearchItemField'][ + indexOfUseProps + ], + properties: {}, + }, + }; +} diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.setting.tsx b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.setting.tsx new file mode 100644 index 000000000..cf6483147 --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.setting.tsx @@ -0,0 +1,161 @@ +import { + SchemaSettings, + SchemaSettingsDataScope, + useCollection, + useCollectionManager, + useCompile, + useDesignable, + useFormBlockContext, +} from '@nocobase/client'; +import { useField, useFieldSchema } from '@tachybase/schema'; +import _ from 'lodash'; +import { useTranslation } from '../../../locale'; +import { isTabSearchCollapsibleInputItem } from '../../tab-search/utils'; + +export const ImageSearchItemFieldSettings = new SchemaSettings({ + name: 'fieldSettings:component:ImageSearchItem', + items: [ + { + name: 'decoratorOptions', + type: 'itemGroup', + hideIfNoChildren: true, + useComponentProps() { + const { t } = useTranslation(); + return { + title: t('Generic properties'), + }; + }, + useChildren() { + return [ + { + name: 'setTheDataScope', + Component: SchemaSettingsDataScope, + useComponentProps() { + const fieldSchema = useFieldSchema(); + const { form } = useFormBlockContext(); + const field = useField(); + const cm = useCollectionManager(); + const c = useCollection(); + const collectionField = + c.getField(fieldSchema['name']) || cm.getCollectionField(fieldSchema['x-collection-field']); + const { dn } = useDesignable(); + + return { + collectionName: collectionField?.target, + 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'], + }, + }); + }, + }; + }, + useVisible() { + const fieldSchema = useFieldSchema(); + return !isTabSearchCollapsibleInputItem(fieldSchema['x-component']); + }, + }, + { + name: 'titleField', + type: 'select', + useComponentProps() { + const fieldSchema = useFieldSchema(); + 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['fieldName']) || + cm.getCollectionField(fieldSchema['x-collection-field']) || + cm.getCollection(fieldCollection + '.' + correlation); + const compile = useCompile(); + const { dn } = useDesignable(); + const targetFields = collectionField?.target ? cm.getCollectionFields(collectionField?.target) : []; + const options = targetFields + .filter((field) => !field?.target && field.type !== 'boolean') + .map((field) => ({ + value: field?.name, + label: compile(field?.uiSchema?.title) || field?.name, + })); + const onTitleFieldChange = (label) => { + const schema = { + ['x-uid']: fieldSchema['x-uid'], + }; + + const fieldNames = { + ...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'], + ...fieldSchema['x-component-props']?.['fieldNames'], + label, + }; + 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, + }); + dn.refresh(); + }; + + return { + key: 'title-field', + title: t('Title field'), + options: options, + value: fieldSchema['x-component-props']?.fieldNames?.label, + onChange: onTitleFieldChange, + }; + }, + useVisible() { + const fieldSchema = useFieldSchema(); + return ( + isTabSearchCollapsibleInputItem(fieldSchema['x-component']) || + fieldSchema['x-component-props']?.['correlation'] + ); + }, + }, + ]; + }, + }, + { + name: 'divider', + type: 'divider', + }, + { + name: 'delete', + type: 'remove', + sort: 100, + useComponentProps() { + const { t } = useTranslation(); + const fieldSchema = useFieldSchema(); + const { dn } = useDesignable(); + return { + removeParentsIfNoChildren: true, + confirm: { + title: t('Delete field'), + }, + breakRemoveOn: (s) => { + if (isTabSearchCollapsibleInputItem(fieldSchema['x-component'])) { + Object.values(s.properties).forEach((value) => { + if (isTabSearchCollapsibleInputItem(value['x-component']) && value.name !== fieldSchema.name) { + delete s.properties[value.name]; + } + }); + dn.emit('patch', { schema: s }); + } + return s['x-component'] === 'TabSearch'; + }, + }; + }, + }, + ], +}); diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.toolbar.tsx b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.toolbar.tsx new file mode 100644 index 000000000..03f81c074 --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.toolbar.tsx @@ -0,0 +1,6 @@ +import { SchemaToolbar } from '@nocobase/client'; +import React from 'react'; + +export const ImageSearchItemToolbar = (props) => { + return ; +}; diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.view.tsx b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.view.tsx new file mode 100644 index 000000000..a9f5c7bc8 --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/ImageSearchItem.view.tsx @@ -0,0 +1,54 @@ +import { SortableItem, css, withDynamicSchemaProps } from '@nocobase/client'; +import { Image, JumboTabs } from 'antd-mobile'; +import React from 'react'; +import { useActionImageSearchItemView } from '../hooks/useAction.ImageSearchItemView'; + +export const ImageSearchItemView = withDynamicSchemaProps( + (props) => { + const { collectionField, Designer, items, onSelect } = useActionImageSearchItemView(props); + + if (!collectionField) { + return null; + } + + return ( + + + + {items.map(({ key, label, imageUrl }) => ( + } /> + ))} + + + ); + }, + { displayName: 'ImageSearchItemView' }, +); + +const ImageDescription = (props) => { + const { srcUrl, label } = props; + return ( +

+ +

+ {label} +

+
+ ); +}; diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/useProps.Optional.ts b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/useProps.Optional.ts new file mode 100644 index 000000000..7d9ffb01c --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/useProps.Optional.ts @@ -0,0 +1,53 @@ +import { useMemo } from 'react'; +import { useCollection, useCollectionManager } from '@nocobase/client'; +import { useFieldSchema } from '@tachybase/schema'; +import _ from 'lodash'; +import { useTabSearchCollapsibleInputItem } from '../../tab-search/components/field-item/hooks'; +import { canBeOptionalField } from '../../tab-search/utils'; + +export const usePropsOptionalImageSearchItemField = () => { + const fieldSchema = useFieldSchema(); + const collection = useCollection(); + const cm = useCollectionManager(); + const optionalFieldList = getOptionalFieldList({ collection }); + const fieldName = fieldSchema['fieldName']; + const collectionField = useMemo(() => collection?.getField(fieldName), [collection, fieldName]); + + const { onSelected } = useTabSearchCollapsibleInputItem(); + + if (!collection) { + return {}; + } + + const result = { list: null, valueKey: '', labelKey: '', filterKey: '' }; + result.valueKey = collectionField?.target ? cm.getCollection(collectionField.target)?.getPrimaryKey() : 'id'; + result.labelKey = fieldSchema['x-component-props']?.fieldNames?.label || result.valueKey; + const fieldInterface = fieldSchema['x-component-props'].interface; + if (canBeOptionalField(fieldInterface)) { + const field = optionalFieldList.find((field) => field.name === fieldSchema['fieldName']); + const operatorMap = { + select: '$in', + multipleSelect: '$anyOf', + checkboxGroup: '$anyOf', + radioGroup: '$in', + }; + + const _list = field?.uiSchema?.enum || []; + result.valueKey = 'value'; + result.labelKey = 'label'; + result.list = _list; + result.filterKey = `${field.name}.${operatorMap[field.interface]}`; + } + return { + list: result.list, + valueKey: result.valueKey, + labelKey: result.labelKey, + onSelected, + filterKey: result.filterKey, + }; +}; + +const getOptionalFieldList = ({ collection }) => { + const currentFields = collection?.fields ?? []; + return currentFields.filter((field) => canBeOptionalField(field.interface) && field.uiSchema.enum); +}; diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/useProps.Related.tsx b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/useProps.Related.tsx new file mode 100644 index 000000000..81169d13e --- /dev/null +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/image-search/search-item/useProps.Related.tsx @@ -0,0 +1,87 @@ +// 关系字段类型 +import { useCollection, useCollectionManager, useDataSourceHeaders, useRequest } from '@nocobase/client'; +import { useField, useFieldSchema } from '@tachybase/schema'; +import { useEffect, useMemo } from 'react'; +import { canBeRelatedField } from '../../tab-search/utils'; +import { useGetSelected } from '../hooks/useSelect'; + +interface requestResultType { + data: { [key: string]: any }[]; +} + +export const usePropsRelatedImageSearchItemField = () => { + const fieldSchema = useFieldSchema(); + const collection = useCollection(); + const field = useField(); + // TODO: 这里需要替换成获取 source id 的方式,不过我们暂时没有用多数据源,就不着急 + const blockProps = { dataSource: 'main' }; + const headers = useDataSourceHeaders(blockProps?.dataSource); + const cm = useCollectionManager(); + + const collectionField = useMemo( + () => collection?.getField(fieldSchema['fieldName'] as any), + [collection, fieldSchema['fieldName']], + ); + + const collectionFieldName = collectionField?.name; + const fieldInterface = fieldSchema['x-component-props'].interface; + const { onSelected } = useGetSelected(); + + const result = { list: null, valueKey: '', labelKey: '', filterKey: '' }; + + result.valueKey = collectionField?.target ? cm.getCollection(collectionField.target)?.getPrimaryKey() : 'id'; + + result.labelKey = fieldSchema['x-component-props']?.fieldNames?.label || result.valueKey; + + const imageShow = fieldSchema['x-component-props']?.fieldNames?.imageShow || 'imageShow'; + + const { data } = useRequestRelatedField({ headers, collectionField, result, imageShow, field, fieldInterface }); + + result.filterKey = `${collectionFieldName}.${result.valueKey}.$in`; + result.list = data?.data || []; + + if (!collectionField) { + return; + } + + return { + list: result.list, + valueKey: result.valueKey, + labelKey: result.labelKey, + filterKey: result.filterKey, + onSelected, + }; +}; + +const useRequestRelatedField = ({ headers, collectionField, result, imageShow, field, fieldInterface }) => { + const { data, run } = useRequest( + { + headers, + resource: collectionField?.target, + action: 'list', + params: { + fields: [result.labelKey, result.valueKey, imageShow, 'type'], + pageSize: 200, + page: 1, + ...field.componentProps?.params, + }, + }, + { + manual: true, + debounceWait: 300, + }, + ); + + useEffect(() => { + if (canBeRelatedField(fieldInterface) && collectionField?.target) { + run(); + } + }, [ + result.labelKey, + result.valueKey, + JSON.stringify(field.componentProps?.params || {}), + canBeRelatedField(fieldInterface), + ]); + + return { data }; +}; diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/components/field-item/TabSearchCollapsibleInputItemAction.ts b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/components/field-item/TabSearchCollapsibleInputItemAction.ts index bbbd0aced..f9b42c43c 100644 --- a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/components/field-item/TabSearchCollapsibleInputItemAction.ts +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/components/field-item/TabSearchCollapsibleInputItemAction.ts @@ -2,10 +2,10 @@ import { useMemo, useRef, useState } from 'react'; import { useFieldSchema } from '@tachybase/schema'; import { useCollection, useCollectionManager, useDesignable, useDesigner } from '@nocobase/client'; import { + canBeCalculatedField, canBeDataField, canBeRelatedField, changFormat, - convertFormat, isTabSearchCollapsibleInputItem, } from '../../utils'; import { useTabSearchCollapsibleInputItem } from './hooks'; @@ -17,6 +17,7 @@ export const useTabSearchCollapsibleInputItemAction = (props) => { const collection = useCollection(); const cm = useCollectionManager(); const { dn } = useDesignable(); + const [needSort, setNeedSort] = useState(false); const collectionField = useMemo( () => collection?.getField(fieldSchema['fieldName'] as any), [collection, fieldSchema['fieldName']], @@ -44,6 +45,11 @@ export const useTabSearchCollapsibleInputItemAction = (props) => { const onSelect = (value) => { let time; let filterKey = `${customLabelKey}.$includes`; + + const canBeCal = canBeCalculatedField(fieldInterface); + if (canBeCal) { + filterKey = `${customLabelKey}.$notEmpty`; + } if (canBeDataField(fieldInterface)) { time = value.split('&').map((value) => JSON.parse(value)); filterKey = `${customLabelKey}.$dateBetween`; @@ -53,7 +59,12 @@ export const useTabSearchCollapsibleInputItemAction = (props) => { const correlation = fieldSchema['x-component-props']['correlation']; filterKey = `${correlation}.${label}.$includes`; } - onSelected(time || [value], filterKey); + + onSelected(time || [value], filterKey, { + canBeCalculatedField: canBeCal, + customLabelKey, + needSort, + }); }; const onSelectChange = (label) => { @@ -107,5 +118,7 @@ export const useTabSearchCollapsibleInputItemAction = (props) => { customLabelKey, fieldInterface, onDateClick, + needSort, + setNeedSort, }; }; diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/components/field-item/TabSearchCollapsibleInputMItem.tsx b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/components/field-item/TabSearchCollapsibleInputMItem.tsx index da3fed830..a4e442056 100644 --- a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/components/field-item/TabSearchCollapsibleInputMItem.tsx +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/components/field-item/TabSearchCollapsibleInputMItem.tsx @@ -1,9 +1,10 @@ -import { SortableItem, withDynamicSchemaProps } from '@nocobase/client'; -import { Grid } from 'antd-mobile'; +import { SortableItem, css, withDynamicSchemaProps } from '@nocobase/client'; +import { Checkbox, Grid, Switch } from 'antd-mobile'; import React from 'react'; import { useTabSearchCollapsibleInputItemAction } from './TabSearchCollapsibleInputItemAction'; import { IButton, IDatePicker, IInput, ISelect } from './TabSearchCollapsibleInputMItemChild'; import { canBeDataField } from '../../utils'; +import { useTranslation } from '../../../../locale'; export const TabSearchCollapsibleInputMItem = withDynamicSchemaProps( (props) => { @@ -12,13 +13,16 @@ export const TabSearchCollapsibleInputMItem = withDynamicSchemaProps( Designer, options, value, + needSort, onSelectChange, onInputChange, onButtonClick, customLabelKey, fieldInterface, onDateClick, + setNeedSort, } = useTabSearchCollapsibleInputItemAction(props); + const { t } = useTranslation(); if (!collectionField) { return null; @@ -26,17 +30,46 @@ export const TabSearchCollapsibleInputMItem = withDynamicSchemaProps( return ( - - - {canBeDataField(fieldInterface) ? ( - - ) : ( - <> - - - - )} - +
+ + + {canBeDataField(fieldInterface) ? ( + + ) : ( + <> + + + + )} + + + {t('sort')} + +
); }, diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/components/field-item/TabSearchCollapsibleInputMItemChild.tsx b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/components/field-item/TabSearchCollapsibleInputMItemChild.tsx index 63f2a1c43..661c6f1e9 100644 --- a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/components/field-item/TabSearchCollapsibleInputMItemChild.tsx +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/components/field-item/TabSearchCollapsibleInputMItemChild.tsx @@ -102,7 +102,7 @@ export const IInput = (props) => { const { options, value, onChange } = props; const { t } = useTranslation(); return ( - 1 ? 2 : 3}> + 1 ? 2 : 3}> { const fieldSchema = useFieldSchema(); const collection = useCollection(); const { getDataBlocks } = useFilterBlock(); - const collectionField = useMemo(() => collection?.getField(fieldSchema.name as any), [collection, fieldSchema.name]); - const onSelected = (value, filterKey) => { + const onSelected = ( + value, + filterKey, + options = { canBeCalculatedField: false, customLabelKey: '', needSort: false }, + ) => { + const { canBeCalculatedField, customLabelKey, needSort = true } = options; const { targets, uid } = findFilterTargets(fieldSchema); getDataBlocks().forEach((block) => { const target = targets.find((target) => target.uid === block.uid); @@ -22,6 +25,12 @@ export const useTabSearchCollapsibleInputItem = () => { storedFilter[key] = { [filterKey]: value, }; + + if (canBeCalculatedField) { + storedFilter[key] = { + [filterKey]: Number(value?.[0] ?? 0), + }; + } } else { if (block.dataLoadingMode === 'manual') { return block.clearData(); @@ -50,14 +59,21 @@ export const useTabSearchCollapsibleInputItem = () => { (value) => value === null || value === undefined || (_.isObject(value) && _.isEmpty(value)), ); - return block.doFilter( - { - ...param, - page: 1, - filter: mergedFilter, - }, - { filters: storedFilter }, - ); + let params1 = { + ...param, + page: 1, + filter: mergedFilter, + }; + if (needSort) { + params1 = { + ...params1, + ['sort[]']: `-${customLabelKey}`, + }; + } else { + params1 = _.omit(params1, ['sort[]']); + } + + return block.doFilter(params1, { filters: storedFilter }); }); }; @@ -68,6 +84,6 @@ export const useTabSearchCollapsibleInputItem = () => { export const useIsMobile = () => { const fieldSchema = useFieldSchema(); - const isMobile = Object.values(fieldSchema.root.properties).find((value) => value['x-component'] === 'MContainer'); + const isMobile = Object.values(fieldSchema.root.properties).some((value) => value['x-component'] === 'MContainer'); return isMobile; }; diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/index.ts b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/index.ts index e88d3e7ab..ab556eabd 100644 --- a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/index.ts +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/index.ts @@ -5,7 +5,6 @@ import { TabSearchBlockInitializer } from './initializer/TabSearchBlockInitializ import { TabSearchFieldItem } from './components/field-item/TabSearchFieldItem'; import { TabSearchCollapsibleInputItem } from './components/field-item/TabSearchCollapsibleInputItem'; import { TabSearchFieldSchemaInitializerGadget } from './initializer/TabSearchFieldSchemaInitializerGadget'; -import { tval } from '../../locale'; import { useTabSearchFieldItemRelatedProps } from './components/field-item/TabSerachFieldItemRelatedProps'; import { useTabSearchFieldItemProps } from './components/field-item/TabSearchFieldItemProps'; import { TabSearchFieldSchemaInitializer } from './initializer/TabSearchFieldSchemaInitializer'; @@ -32,16 +31,10 @@ class PluginTabSearch extends Plugin { }); this.app.schemaInitializerManager.add(TabSearchFieldSchemaInitializer); this.schemaSettingsManager.add(TabSearchItemFieldSettings); - this.app.schemaInitializerManager.addItem('mobilePage:addBlock', 'filterBlocks', { - title: tval('Filter blocks'), - type: 'itemGroup', - children: [ - { - name: 'tabSearch', - title: 'tabSearch', - Component: 'TabSearchBlockInitializer', - }, - ], + this.app.schemaInitializerManager.addItem('mobilePage:addBlock', 'filterBlocks.tabSearch', { + name: 'tabSearch', + title: 'tabSearch', + Component: 'TabSearchBlockInitializer', }); this.app.schemaInitializerManager.addItem('page:addBlock', 'filterBlocks.tabSearch', { name: 'tabSearch', diff --git a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/utils.ts b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/utils.ts index 19cf3bf3a..9626782e3 100644 --- a/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/utils.ts +++ b/packages/plugins/@hera/plugin-mobile/src/client/schma-component/tab-search/utils.ts @@ -10,8 +10,6 @@ const canBeSearchFields = [ 'phone', 'email', 'url', - 'integer', - 'number', 'percent', 'password', 'color', @@ -20,6 +18,8 @@ const canBeSearchFields = [ 'sequence', ]; +const canBeCalculatedFields = ['integer', 'number']; + export const isTabSearchCollapsibleInputItem = (component: string) => component === 'TabSearchCollapsibleInputItem' || component === 'TabSearchCollapsibleInputMItem'; @@ -27,6 +27,7 @@ export const canBeOptionalField = (_interface: string) => canBeOptionalFields.in 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 canBeCalculatedField = (_interface: string) => canBeCalculatedFields.includes(_interface); export const convertFormat = (currentDate) => { return dayjs(currentDate).format('YYYY-MM-DD'); diff --git a/packages/plugins/@hera/plugin-mobile/src/locale/en-US.json b/packages/plugins/@hera/plugin-mobile/src/locale/en-US.json index 3f5c86d55..556f51607 100644 --- a/packages/plugins/@hera/plugin-mobile/src/locale/en-US.json +++ b/packages/plugins/@hera/plugin-mobile/src/locale/en-US.json @@ -11,5 +11,7 @@ "Configure fields": "Configure fields", "Configure columns": "Configure columns", "Add block": "Add block", - "all": "all" + "all": "all", + "AllProducts": "AllProducts", + "sort":"sort" } diff --git a/packages/plugins/@hera/plugin-mobile/src/locale/zh-CN.json b/packages/plugins/@hera/plugin-mobile/src/locale/zh-CN.json index ab456be10..d055f6fd1 100644 --- a/packages/plugins/@hera/plugin-mobile/src/locale/zh-CN.json +++ b/packages/plugins/@hera/plugin-mobile/src/locale/zh-CN.json @@ -11,5 +11,7 @@ "Configure fields": "配置字段", "Configure columns": "配置字段", "Add block": "创建区块", - "all": "全部" + "all": "全部", + "AllProducts": "全部商品", + "sort": "排序" } diff --git a/packages/plugins/@hera/plugin-sancongtou/.npmignore b/packages/plugins/@hera/plugin-sancongtou/.npmignore new file mode 100644 index 000000000..65f5e8779 --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/.npmignore @@ -0,0 +1,2 @@ +/node_modules +/src diff --git a/packages/plugins/@hera/plugin-sancongtou/README.md b/packages/plugins/@hera/plugin-sancongtou/README.md new file mode 100644 index 000000000..e52e4f3b8 --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/README.md @@ -0,0 +1 @@ +# @hera/plugin-sancongtou diff --git a/packages/plugins/@hera/plugin-sancongtou/client.d.ts b/packages/plugins/@hera/plugin-sancongtou/client.d.ts new file mode 100644 index 000000000..6c459cbac --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/client.d.ts @@ -0,0 +1,2 @@ +export * from './dist/client'; +export { default } from './dist/client'; diff --git a/packages/plugins/@hera/plugin-sancongtou/client.js b/packages/plugins/@hera/plugin-sancongtou/client.js new file mode 100644 index 000000000..b6e3be70e --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/client.js @@ -0,0 +1 @@ +module.exports = require('./dist/client/index.js'); diff --git a/packages/plugins/@hera/plugin-sancongtou/package.json b/packages/plugins/@hera/plugin-sancongtou/package.json new file mode 100644 index 000000000..7acb17e5b --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/package.json @@ -0,0 +1,18 @@ +{ + "name": "@hera/plugin-sancongtou", + "displayName": "Mobile client: Sancongtou Customization", + "version": "0.21.24", + "description": "Provide mobile client customization for the sancongtou project.", + "main": "dist/server/index.js", + "dependencies": { + "@ant-design/icons": "^5.3.6", + "antd-mobile": "^5.35.0" + }, + "peerDependencies": { + "@nocobase/client": "0.x", + "@nocobase/server": "0.x", + "@nocobase/test": "0.x" + }, + "description.zh-CN": "提供移动端: 定制化的三聪头项目页面,.", + "displayName.zh-CN": "移动端: 三聪头,定制化页面配置" +} diff --git a/packages/plugins/@hera/plugin-sancongtou/server.d.ts b/packages/plugins/@hera/plugin-sancongtou/server.d.ts new file mode 100644 index 000000000..c41081ddc --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/server.d.ts @@ -0,0 +1,2 @@ +export * from './dist/server'; +export { default } from './dist/server'; diff --git a/packages/plugins/@hera/plugin-sancongtou/server.js b/packages/plugins/@hera/plugin-sancongtou/server.js new file mode 100644 index 000000000..972842039 --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/server.js @@ -0,0 +1 @@ +module.exports = require('./dist/server/index.js'); diff --git a/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_dx_content.png b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_dx_content.png new file mode 100644 index 000000000..dd8dcecf4 Binary files /dev/null and b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_dx_content.png differ diff --git a/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_dx_header.png b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_dx_header.png new file mode 100644 index 000000000..784c29173 Binary files /dev/null and b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_dx_header.png differ diff --git a/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_lt_content.png b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_lt_content.png new file mode 100644 index 000000000..ac218bc11 Binary files /dev/null and b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_lt_content.png differ diff --git a/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_lt_header.png b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_lt_header.png new file mode 100644 index 000000000..a65ed6393 Binary files /dev/null and b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_lt_header.png differ diff --git a/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_yd_content.png b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_yd_content.png new file mode 100644 index 000000000..74b34fa29 Binary files /dev/null and b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_yd_content.png differ diff --git a/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_yd_header.png b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_yd_header.png new file mode 100644 index 000000000..49843ccd2 Binary files /dev/null and b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/detail_yd_header.png differ diff --git a/packages/plugins/@hera/plugin-sancongtou/src/client/assets/download.svg b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/download.svg new file mode 100644 index 000000000..69e379166 --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/src/client/assets/download.svg @@ -0,0 +1,2 @@ +

江苏联通 王炸卡

  • 29/月
  • 135G流量
  • 100分钟
  • 一次性充值50享优惠

长按识别

\ No newline at end of file diff --git a/packages/plugins/@hera/plugin-sancongtou/src/client/components/ShareProduct.tsx b/packages/plugins/@hera/plugin-sancongtou/src/client/components/ShareProduct.tsx new file mode 100644 index 000000000..c06529116 --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/src/client/components/ShareProduct.tsx @@ -0,0 +1,63 @@ +import { Modal, Toast, Image, Button } from 'antd-mobile'; +import { ShareAltOutlined } from '@ant-design/icons'; +import React from 'react'; +import downloadImage from '../assets/download.svg'; +import { CustomComponentType } from '@hera/plugin-core/client'; + +export const ShareProduct = () => { + return ( + + ); +}; + +ShareProduct.displayName = 'ShareProduct'; +ShareProduct.__componentType = CustomComponentType.CUSTOM_FORM_ITEM; +ShareProduct.__componentLabel = '移动端-三聪头-分享产品'; + +const showModal = () => { + Modal.show({ + bodyStyle: { + background: 'transparent', + }, + content: , + closeOnMaskClick: true, + actions: [ + { + key: 'share', + text: '复制推广链接', + primary: true, + }, + ], + onAction: async () => { + await copyHrefToClipboard(); + Toast.show('复制成功!'); + }, + }); +}; + +const ImageShow = () => { + return ; +}; + +async function copyHrefToClipboard() { + const location = window.location; + const href = location.href; + try { + await navigator.clipboard.writeText(href); + console.log('文本已复制到剪贴板', href); + } catch (err) { + console.error('无法复制文本: ', err); + } +} diff --git a/packages/plugins/@hera/plugin-sancongtou/src/client/components/Test.tsx b/packages/plugins/@hera/plugin-sancongtou/src/client/components/Test.tsx new file mode 100644 index 000000000..5edf2a3a7 --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/src/client/components/Test.tsx @@ -0,0 +1,11 @@ +import { Button } from 'antd-mobile'; +import React from 'react'; + +export const TestComponent = () => { + const handleClick = () => {}; + return ( + + ); +}; diff --git a/packages/plugins/@hera/plugin-sancongtou/src/client/index.tsx b/packages/plugins/@hera/plugin-sancongtou/src/client/index.tsx new file mode 100644 index 000000000..aaaa43283 --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/src/client/index.tsx @@ -0,0 +1,33 @@ +import { Plugin } from '@nocobase/client'; +import { ProductDetail } from './pages/ProductDetail'; +import { ShareProduct } from './components/ShareProduct'; + +export class PluginSancongtouClient extends Plugin { + async afterAdd() { + // await this.app.pm.add() + } + + async beforeLoad() {} + + // You can get and modify the app instance here + async load() { + this.addRoutes(); + this.app.addComponents({ + ShareProduct, + }); + console.log(this.app); + // this.app.addComponents({}) + // this.app.addScopes({}) + // this.app.addProvider() + // this.app.addProviders() + } + + addRoutes() { + this.app.router.add('sancongtou/detail', { + path: '/mobile/products/detail', + Component: ProductDetail, + }); + } +} + +export default PluginSancongtouClient; diff --git a/packages/plugins/@hera/plugin-sancongtou/src/client/pages/ProductDetail.tsx b/packages/plugins/@hera/plugin-sancongtou/src/client/pages/ProductDetail.tsx new file mode 100644 index 000000000..1389cf101 --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/src/client/pages/ProductDetail.tsx @@ -0,0 +1,49 @@ +import { NavBar, Image } from 'antd-mobile'; +import React from 'react'; +import detailHeaderImpage from '../assets/detail_lt_header.png'; +import detailContentImage from '../assets/detail_lt_content.png'; +import { css } from '@nocobase/client'; + +export const ProductDetail = () => { + const onBack = () => { + const history = window.history; + history.back(); + }; + + return ( + <> + + 产品详情 + +
+ + +
+ + ); +}; diff --git a/packages/plugins/@hera/plugin-sancongtou/src/index.ts b/packages/plugins/@hera/plugin-sancongtou/src/index.ts new file mode 100644 index 000000000..7e74612df --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/src/index.ts @@ -0,0 +1,2 @@ +export * from './server'; +export { default } from './server'; diff --git a/packages/plugins/@hera/plugin-sancongtou/src/server/collections/.gitkeep b/packages/plugins/@hera/plugin-sancongtou/src/server/collections/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/plugins/@hera/plugin-sancongtou/src/server/index.ts b/packages/plugins/@hera/plugin-sancongtou/src/server/index.ts new file mode 100644 index 000000000..b68aea57f --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/src/server/index.ts @@ -0,0 +1 @@ +export { default } from './plugin'; diff --git a/packages/plugins/@hera/plugin-sancongtou/src/server/plugin.ts b/packages/plugins/@hera/plugin-sancongtou/src/server/plugin.ts new file mode 100644 index 000000000..802f9ec23 --- /dev/null +++ b/packages/plugins/@hera/plugin-sancongtou/src/server/plugin.ts @@ -0,0 +1,19 @@ +import { Plugin } from '@nocobase/server'; + +export class PluginSancongtouServer extends Plugin { + async afterAdd() {} + + async beforeLoad() {} + + async load() {} + + async install() {} + + async afterEnable() {} + + async afterDisable() {} + + async remove() {} +} + +export default PluginSancongtouServer; diff --git a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/container/style.ts b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/container/style.ts index f03c480ba..e8db45b3c 100644 --- a/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/container/style.ts +++ b/packages/plugins/@tachybase/plugin-mobile-client/src/client/core/schema/components/container/style.ts @@ -22,7 +22,7 @@ const useStyles = createStyles(({ token, css }) => { & > .general-schema-designer { --nb-designer-top: ${token.marginMD}px; } - position: absolute; + position: fixed; background: ${token.colorBgContainer}; width: 100%; bottom: 0; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dd677e1ca..f649ced39 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1764,6 +1764,24 @@ importers: specifier: ^6.26.0 version: 6.35.2 + packages/plugins/@hera/plugin-sancongtou: + dependencies: + '@ant-design/icons': + specifier: ^5.3.6 + version: 5.3.6(react-dom@18.2.0)(react@18.2.0) + '@nocobase/client': + specifier: 0.x + version: link:../../../core/client + '@nocobase/server': + specifier: 0.x + version: link:../../../core/server + '@nocobase/test': + specifier: 0.x + version: link:../../../core/test + antd-mobile: + specifier: ^5.35.0 + version: 5.35.0(react-dom@18.2.0)(react@18.2.0) + packages/plugins/@nocobase/plugin-acl: dependencies: '@nocobase/acl': @@ -8708,7 +8726,6 @@ packages: resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==} dependencies: '@floating-ui/utils': 0.2.1 - dev: true /@floating-ui/dom@0.4.5: resolution: {integrity: sha512-b+prvQgJt8pieaKYMSJBXHxX/DYwdLsAWxKYqnO5dO2V4oo/TYBZJAUQCVNjTWWsrs6o4VDrNcP9+E70HAhJdw==} @@ -8720,7 +8737,6 @@ packages: dependencies: '@floating-ui/core': 1.6.0 '@floating-ui/utils': 0.2.1 - dev: true /@floating-ui/react-dom-interactions@0.3.1(@types/react@18.2.75)(react-dom@18.1.0)(react@18.1.0): resolution: {integrity: sha512-tP2KEh7EHJr5hokSBHcPGojb+AorDNUf0NYfZGg/M+FsMvCOOsSEeEF0O1NDfETIzDnpbHnCs0DuvCFhSMSStg==} @@ -8780,7 +8796,6 @@ packages: /@floating-ui/utils@0.2.1: resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} - dev: true /@formily/antd-v5@1.2.0(@types/react-dom@18.2.25)(@types/react@18.2.79)(antd@5.16.1)(react-dom@18.2.0)(react-is@18.3.1)(react@18.2.0)(typescript@4.9.5): resolution: {integrity: sha512-6K1fV7pwvy7j5KoO/5dEe5llosann7211I/bH/WJkLQtDSDK+XLFUQ4/lU7CdNpwHMrspzXS3lF82EM3hGyM8w==} @@ -10211,7 +10226,6 @@ packages: '@react-spring/shared': 9.6.1(react@18.2.0) '@react-spring/types': 9.6.1 react: 18.2.0 - dev: true /@react-spring/core@9.6.1(react@18.2.0): resolution: {integrity: sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==} @@ -10223,11 +10237,9 @@ packages: '@react-spring/shared': 9.6.1(react@18.2.0) '@react-spring/types': 9.6.1 react: 18.2.0 - dev: true /@react-spring/rafz@9.6.1: resolution: {integrity: sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ==} - dev: true /@react-spring/shared@9.6.1(react@18.2.0): resolution: {integrity: sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==} @@ -10237,11 +10249,9 @@ packages: '@react-spring/rafz': 9.6.1 '@react-spring/types': 9.6.1 react: 18.2.0 - dev: true /@react-spring/types@9.6.1: resolution: {integrity: sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==} - dev: true /@react-spring/web@9.6.1(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-X2zR6q2Z+FjsWfGAmAXlQaoUHbPmfuCaXpuM6TcwXPpLE1ZD4A1eys/wpXboFQmDkjnrlTmKvpVna1MjWpZ5Hw==} @@ -10255,7 +10265,6 @@ packages: '@react-spring/types': 9.6.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - dev: true /@redis/bloom@1.2.0(@redis/client@1.5.14): resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} @@ -13114,7 +13123,6 @@ packages: /@use-gesture/core@10.3.0: resolution: {integrity: sha512-rh+6MND31zfHcy9VU3dOZCqGY511lvGcfyJenN4cWZe0u1BH6brBpBddLVXhF2r4BMqWbvxfsbL7D287thJU2A==} - dev: true /@use-gesture/react@10.3.0(react@18.2.0): resolution: {integrity: sha512-3zc+Ve99z4usVP6l9knYVbVnZgfqhKah7sIG+PS2w+vpig2v2OLct05vs+ZXMzwxdNCMka8B+8WlOo0z6Pn6DA==} @@ -13123,7 +13131,6 @@ packages: dependencies: '@use-gesture/core': 10.3.0 react: 18.2.0 - dev: true /@vercel/ncc@0.36.1: resolution: {integrity: sha512-S4cL7Taa9yb5qbv+6wLgiKVZ03Qfkc4jGRuiUQMQ8HGBD5pcNRnHeYM33zBvJE4/zJGjJJ8GScB+WmTsn9mORw==} @@ -13140,7 +13147,7 @@ packages: '@babel/plugin-transform-react-jsx-self': 7.24.1(@babel/core@7.24.4) '@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.4) react-refresh: 0.14.0 - vite: 4.5.2(@types/node@20.12.7)(less@4.1.3) + vite: 4.5.2(@types/node@20.12.2)(less@4.1.3) transitivePeerDependencies: - supports-color @@ -13453,7 +13460,6 @@ packages: resize-observer-polyfill: 1.5.1 screenfull: 5.2.0 tslib: 2.6.2 - dev: true /ahooks@3.7.8(react@18.2.0): resolution: {integrity: sha512-e/NMlQWoCjaUtncNFIZk3FG1ImSkV/JhScQSkTqnftakRwdfZWSw6zzoWSG9OMYqPNs2MguDYBUFFC6THelWXA==} @@ -13683,11 +13689,9 @@ packages: /antd-mobile-icons@0.3.0: resolution: {integrity: sha512-rqINQpJWZWrva9moCd1Ye695MZYWmqLPE+bY8d2xLRy7iSQwPsinCdZYjpUPp2zL/LnKYSyXxP2ut2A+DC+whQ==} - dev: true /antd-mobile-v5-count@1.0.1: resolution: {integrity: sha512-YGsiEDCPUDz3SzfXi6gLZn/HpeSMW+jgPc4qiYUr1fSopg3hkUie2TnooJdExgfiETHefH3Ggs58He0OVfegLA==} - dev: true /antd-mobile@5.35.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UYr8T2Tlw3eXdyDYpWrPu54RWDcon4biI5VWHtMp9XxmSP5tzypRxoseh1ZGE28boQq9FKpSCC6FZ9rN0HXnQg==} @@ -13716,7 +13720,6 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) transitivePeerDependencies: - react-dom - dev: true /antd-style@3.4.5(@types/react@18.2.79)(antd@5.16.1)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-6aC4P9XyuVy0O7eZ+HZXd8GbbFX9HgzsXsJ341ihJhgqrfsQZNx8lDQvS2kCV6ao99QqtyTDphK9gWOgV2bHEw==} @@ -22753,7 +22756,6 @@ packages: /nano-memoize@3.0.16: resolution: {integrity: sha512-JyK96AKVGAwVeMj3MoMhaSXaUNqgMbCRSQB3trUV8tYZfWEzqUBKdK1qJpfuNXgKeHOx1jv/IEYTM659ly7zUA==} - dev: true /nanoid@3.3.4: resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} @@ -25224,7 +25226,6 @@ packages: rc-util: 5.39.1(react-dom@18.2.0)(react@18.2.0) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - dev: true /rc-field-form@1.44.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-el7w87fyDUsca63Y/s8qJcq9kNkf/J5h+iTdqG5WsSHLH0e6Usl7QuYSmSVzJMgtp40mOVZIY/W/QP9zwrp1FA==} @@ -26945,7 +26946,6 @@ packages: /runes2@1.1.4: resolution: {integrity: sha512-LNPnEDPOOU4ehF71m5JoQyzT2yxwD6ZreFJ7MxZUAoMKNMY1XrAo60H1CUoX5ncSm0rIuKlqn9JZNRrRkNou2g==} - dev: true /rw@1.3.3: resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} @@ -27809,7 +27809,6 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: react: 18.2.0 - dev: true /static-extend@0.1.2: resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} @@ -30017,7 +30016,6 @@ packages: rollup: 3.29.4 optionalDependencies: fsevents: 2.3.3 - dev: false /vite@4.5.2(@types/node@20.12.7)(less@4.1.3): resolution: {integrity: sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==} @@ -30054,6 +30052,7 @@ packages: rollup: 3.29.4 optionalDependencies: fsevents: 2.3.3 + dev: true /vite@5.1.5(@types/node@20.12.7)(sass@1.75.0): resolution: {integrity: sha512-BdN1xh0Of/oQafhU+FvopafUp6WaYenLU/NFoL5WyJL++GxkNfieKzBhM24H3HVsPQrlAqB7iJYTHabzaRed5Q==}