diff --git a/es/components/map/locate/index.js b/es/components/map/locate/index.js index aad59f40..2db9faa1 100644 --- a/es/components/map/locate/index.js +++ b/es/components/map/locate/index.js @@ -13,10 +13,10 @@ export default function Locate(props) { const searchRef = useRef(); // 这里不能用useFeatures,因为无法引用lib里面的provider,引用src注入是不行的 const featureGeo = useFeatures().geo; - useEffect(() => { + const searchFn = () => { if (searchValue?.length > 1) { setSearchLoading(true); - featureGeo.searchPoi(searchValue).then((result) => { + featureGeo.searchPoi(searchValue, props.areaId, undefined, 0, 10).then((result) => { setSearchLoading(false); setPois(result.map((ele, idx) => ({ ...ele, @@ -33,9 +33,14 @@ export default function Locate(props) { else { setPois(undefined); } + }; + let handle; + useEffect(() => { + handle = setTimeout(searchFn, 1000); }, [searchValue]); const center = currentPoi?.coordinate || props.coordinate; const Locate = ( { + clearTimeout(handle); setSearchValue(e.target.value); }} prefix={} onFocus={() => { setMode('searchPoi'); @@ -76,6 +81,7 @@ export default function Locate(props) { return ( diff --git a/es/components/map/locate/web.module.less b/es/components/map/locate/web.module.less index e43fa072..7a5e0904 100644 --- a/es/components/map/locate/web.module.less +++ b/es/components/map/locate/web.module.less @@ -1,6 +1,8 @@ .location-list { width: 100%; height: 100%; + overflow-y: scroll; + max-height: 600px; display: flex; flex-direction: column; diff --git a/es/components/map/openlayer/index.js b/es/components/map/openlayer/index.js index ed741a68..f76b7a4b 100644 --- a/es/components/map/openlayer/index.js +++ b/es/components/map/openlayer/index.js @@ -1,4 +1,5 @@ import { assert } from 'oak-domain/lib/utils/assert'; +import { wgs84togcj02 } from 'oak-domain/lib/utils/geo'; import React, { useEffect, useState } from 'react'; import OlMap from 'ol/Map'; import Feature from 'ol/Feature'; @@ -27,6 +28,7 @@ export default function Map(props) { layers: [ new TileLayer({ source: new XYZ({ + // 用的高德的瓦片,gcj02坐标 url: 'http://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}', wrapX: false, }), @@ -36,7 +38,7 @@ export default function Map(props) { }), ], view: new View({ - center: fromLonLat(props.center || DEFAULT_CENTER), + center: fromLonLat(wgs84togcj02(props.center || DEFAULT_CENTER)), zoom: props.zoom || DEFAULT_ZOOM, minZoom: props.unzoomable ? props.zoom : undefined, maxZoom: props.unzoomable ? props.zoom : undefined, @@ -59,7 +61,7 @@ export default function Map(props) { } if (!props.center) { locate().then(({ latitude, longitude }) => { - map2.getView().setCenter(fromLonLat([longitude, latitude])); + map2.getView().setCenter(fromLonLat(wgs84togcj02([longitude, latitude]))); }); } setMap(map2); @@ -71,13 +73,13 @@ export default function Map(props) { const oc2 = toLonLat(originCenter); if (oc2[0] !== props.center[0] || oc2[1] !== props.center[1]) { map.getView().animate({ - center: fromLonLat(props.center), + center: fromLonLat(wgs84togcj02(props.center)), duration: 500, }); } } else { - map.getView().setCenter(fromLonLat(props.center)); + map.getView().setCenter(fromLonLat(wgs84togcj02(props.center))); } } }, [props.center]); @@ -92,11 +94,11 @@ export default function Map(props) { let feature = markerLayer.getSource().getFeatureById('markers'); if (feature) { // feature.setGeometry(new MultiPoint(props.markers.map(ele => fromLonLat(ele)))); - feature.setGeometry(new Point(fromLonLat(props.markers[0]))); + feature.setGeometry(new Point(fromLonLat(wgs84togcj02(props.markers[0])))); } else { // feature = new Feature(new MultiPoint(props.markers.map(ele => fromLonLat(ele)))); - feature = new Feature(new Point(fromLonLat(props.markers[0]))); + feature = new Feature(new Point(fromLonLat(wgs84togcj02(props.markers[0])))); feature.setStyle(new Style({ image: new Circle({ // 点的颜色 diff --git a/es/components/relation/path/list/locales/zh-CN.json b/es/components/relation/path/list/locales/zh-CN.json deleted file mode 100644 index dcfb881a..00000000 --- a/es/components/relation/path/list/locales/zh-CN.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "tips": { - "remove": "如果关系路径上关联了actionAuth或者relationAuth,删除将会失败" - } -} diff --git a/es/features/geo.d.ts b/es/features/geo.d.ts index ba1290b8..f6f3f16c 100644 --- a/es/features/geo.d.ts +++ b/es/features/geo.d.ts @@ -5,7 +5,7 @@ import { Cache } from './cache'; export declare class Geo extends Feature { private cache; constructor(cache: Cache); - searchPoi(name: string, areaId?: string, typeCode?: string): Promise<{ + searchPoi(name: string, areaId?: string, typeCode?: string, indexFrom?: number, count?: number): Promise<{ poiName: string; areaId: string; latitude: number; diff --git a/es/features/geo.js b/es/features/geo.js index 036ae00f..388ef58e 100644 --- a/es/features/geo.js +++ b/es/features/geo.js @@ -5,12 +5,14 @@ export class Geo extends Feature { super(); this.cache = cache; } - async searchPoi(name, areaId, typeCode) { + async searchPoi(name, areaId, typeCode, indexFrom, count) { const { result } = await this.cache.exec('geoService', { api: 'geo', params: { name, - areaId + areaId, + indexFrom, + count, } }); return result; diff --git a/es/features/runningTree.d.ts b/es/features/runningTree.d.ts index 600b4748..a46ae4cc 100644 --- a/es/features/runningTree.d.ts +++ b/es/features/runningTree.d.ts @@ -156,7 +156,7 @@ declare class ListNode, ids: string[], action?: ED[T]['Action']): void; - composeOperations(): Array<{ + composeOperations(includeModiBranch?: boolean): Array<{ entity: keyof ED; operation: ED[keyof ED]['Operation']; }> | undefined; @@ -214,7 +214,12 @@ declare class SingleNode | undefined; @@ -249,7 +254,7 @@ declare class VirtualNode extends Node; - composeOperations(): Array<{ + composeOperations(includeModiBranch?: boolean): Array<{ entity: keyof ED; operation: ED[keyof ED]['Operation']; }> | undefined; diff --git a/es/features/runningTree.js b/es/features/runningTree.js index b961c58f..1ea8e217 100644 --- a/es/features/runningTree.js +++ b/es/features/runningTree.js @@ -827,14 +827,14 @@ class ListNode extends EntityNode { ids.forEach((id) => this.updateItemInner(lsn, data, id, action)); this.setDirty(); } - composeOperations() { + composeOperations(includeModiBranch) { if (!this.dirty) { return; } const intrinsticFilter = this.getIntrinsticFilters(); const ulmLsn = this.ulManager.maxLsn; for (const id in this.children) { - const childOperations = this.children[id].composeOperations(true); + const childOperations = this.children[id].composeOperations(includeModiBranch); if (childOperations) { childOperations.forEach((childOperation) => this.ulManager.push(ulmLsn + 100, childOperation.operation)); } @@ -1315,13 +1315,22 @@ class SingleNode extends EntityNode { }); super.setDirty(); } - composeOperations(fromParent) { + /** + * + * @param includeModiBranch 如果显式置了boolean,代表只要考虑前项路径或者后项路径 + * @returns + */ + composeOperations(includeModiBranch) { if (this.dirty) { const lsnMax = this.ulManager.maxLsn; for (const ele in this.children) { + if (ele.includes(MODI_NEXT_PATH_SUFFIX) && false === includeModiBranch || + (!ele.includes(MODI_NEXT_PATH_SUFFIX) && true === includeModiBranch)) { + continue; + } const ele2 = ele.includes(':') ? ele.slice(0, ele.indexOf(':')) : ele; const child = this.children[ele]; - const childOperations = child.composeOperations(true); + const childOperations = child.composeOperations(includeModiBranch); if (childOperations) { if (child instanceof SingleNode) { assert(childOperations.length === 1); @@ -1680,14 +1689,18 @@ class VirtualNode extends Node { throw err; } } - composeOperations() { + composeOperations(includeModiBranch) { /** * 当一个virtualNode有多个子结点,而这些子结点的前缀一致时,标识这些子结点其实是指向同一个对象,此时需要合并 */ const operationss = []; const operationDict = {}; for (const ele in this.children) { - const operation = this.children[ele].composeOperations(true); + if (ele.includes(MODI_NEXT_PATH_SUFFIX) && false === includeModiBranch || + (!ele.includes(MODI_NEXT_PATH_SUFFIX) && true === includeModiBranch)) { + continue; + } + const operation = this.children[ele].composeOperations(includeModiBranch); if (operation) { const idx = ele.indexOf(':') !== -1 ? ele.slice(0, ele.indexOf(':')) : ele; if (operationDict[idx]) { @@ -1928,7 +1941,7 @@ export class RunningTree extends Feature { redoBranchOperations(path, allowInTxn) { const { root } = analyzePath(path); const rootNode = this.root[root]; - const opers = rootNode.composeOperations(); + const opers = rootNode.composeOperations(path.includes(MODI_NEXT_PATH_SUFFIX)); if (opers) { this.cache.redoOperation(opers); } diff --git a/es/types/Page.d.ts b/es/types/Page.d.ts index 14d7d46f..cc293a2f 100644 --- a/es/types/Page.d.ts +++ b/es/types/Page.d.ts @@ -37,32 +37,63 @@ type FeatureDef = ED[T]['CreateSingle']['data'] | (() => ED[T]['CreateSingle']['data']); interface ComponentOption, FrontCxt extends SyncContext, AD extends Record>, FD extends Record, FormedData extends Record, TData extends DataOption, TProperty extends DataOption, TMethod extends Record, EMethod extends Record = {}> { + /** + * 是否为列表组件 + */ isList?: IsList; zombie?: true; getTotal?: { max: number; deviceWidth?: DevideWidth | 'all'; } | number; + /** + * 组件所关联的实体 + */ entity?: T | ((this: ComponentPublicThisType) => T); path?: string; + /** + * 需要监听的features + */ features?: FeatureDef[]; cascadeActions?: (this: ComponentPublicThisType) => { [K in keyof ED[T]['Schema']]?: ActionDef[]; }; + /** + * 需要校验的actions + */ actions?: ActionDef[] | ((this: ComponentPublicThisType) => ActionDef[]); + /** + * 需要获取的数据字段 + * 若嵌套在list中,需要保证父list组件能够覆盖子组件的projection + */ projection?: ED[T]['Selection']['data'] | ((this: ComponentPublicThisType) => ED[T]['Selection']['data'] | undefined); append?: boolean; + /** + * 分页信息 + */ pagination?: Pick | Array & { deviceWidth: DevideWidth; }>; + /** + * 过滤器 + */ filters?: Array<{ filter: NonNullable | ((this: ComponentPublicThisType) => ED[T]['Selection']['filter'] | undefined); '#name'?: string; }>; + /** + * 排序器 + */ sorters?: Array<{ sorter: NonNullable[number] | ((this: ComponentPublicThisType) => ED[T]['Selection']['sorter'] | NonNullable[number]); '#name'?: string; }>; + /** + * 表单数据 + * 在组件渲染前会调用此方法,将返回的数据放入组件的data中 + * @param options 组件获取到的数据 + * @returns 返回给页面的数据 + */ formData?: (options: { data: IsList extends true ? RowWithActions[] : RowWithActions; origin?: IsList extends true ? RowWithActions[] : RowWithActions; @@ -74,8 +105,17 @@ interface ComponentOption FormedData; ns?: T | T[]; + /** + * 声明的State数据 + */ data?: ((this: ComponentPublicThisType) => TData) | TData; + /** + * 需要参数传入的默认prop参数 + */ properties?: TProperty; + /** + * 自定义方法 + */ methods?: TMethod; } export type MiniprogramStyleMethods = { @@ -106,17 +146,63 @@ export type ComponentFullThisType & OakListComponentMethods & OakSingleComponentMethods; export type OakComponentOption, FrontCxt extends SyncContext, AD extends Record>>, FD extends Record, FormedData extends Record, TData extends Record, TProperty extends DataOption, TMethod extends Record, EMethod extends Record = {}> = ComponentOption & Partial<{ + /** + * 生命周期回调 + */ lifetimes: { + /** + * 在组件实例刚刚被创建时执行 + * sync only + */ created?(): void; + /** + * 在组件实例进入页面节点树时执行 + * sync only + */ attached?(): void; + /** + * 在组件实例进入页面节点树时执行 + * async or sync + */ ready?(): any; + /** + * 小程序专用,在组件实例被移动到节点树另一个位置时执行 + * sync only + */ moved?(): void; + /** + * 在组件实例被从页面节点树移除时执行 + * sync only + */ detached?(): void; + /** + * 小程序专用,每当组件方法抛出错误时执行 + * sync only + * @param err 错误信息 + */ error?(err: Error): void; + /** + * 组件所在的页面被展示时执行 + * sync only + */ show?(): void; + /** + * 组件所在的页面被隐藏时执行 + * sync only + */ hide?(): void; + /** + * 组件所在的页面尺寸变化时执行 + * sync only + * @param size 页面尺寸信息 + */ resize?(size: WechatMiniprogram.Page.IResizeOption): void; }; + /** + * 监听器,监听State或者Props的变化 + * 名称:监听的属性名 + * 回调:监听的回调函数 (prev, next) => void + */ listeners: Record, prev: Record, next: Record) => void>; }> & Partial<{ wechatMp: { diff --git a/lib/features/geo.d.ts b/lib/features/geo.d.ts index ba1290b8..f6f3f16c 100644 --- a/lib/features/geo.d.ts +++ b/lib/features/geo.d.ts @@ -5,7 +5,7 @@ import { Cache } from './cache'; export declare class Geo extends Feature { private cache; constructor(cache: Cache); - searchPoi(name: string, areaId?: string, typeCode?: string): Promise<{ + searchPoi(name: string, areaId?: string, typeCode?: string, indexFrom?: number, count?: number): Promise<{ poiName: string; areaId: string; latitude: number; diff --git a/lib/features/geo.js b/lib/features/geo.js index f83630f0..b90114e0 100644 --- a/lib/features/geo.js +++ b/lib/features/geo.js @@ -8,12 +8,14 @@ class Geo extends Feature_1.Feature { super(); this.cache = cache; } - async searchPoi(name, areaId, typeCode) { + async searchPoi(name, areaId, typeCode, indexFrom, count) { const { result } = await this.cache.exec('geoService', { api: 'geo', params: { name, - areaId + areaId, + indexFrom, + count, } }); return result; diff --git a/lib/features/runningTree.d.ts b/lib/features/runningTree.d.ts index 600b4748..a46ae4cc 100644 --- a/lib/features/runningTree.d.ts +++ b/lib/features/runningTree.d.ts @@ -156,7 +156,7 @@ declare class ListNode, ids: string[], action?: ED[T]['Action']): void; - composeOperations(): Array<{ + composeOperations(includeModiBranch?: boolean): Array<{ entity: keyof ED; operation: ED[keyof ED]['Operation']; }> | undefined; @@ -214,7 +214,12 @@ declare class SingleNode | undefined; @@ -249,7 +254,7 @@ declare class VirtualNode extends Node; - composeOperations(): Array<{ + composeOperations(includeModiBranch?: boolean): Array<{ entity: keyof ED; operation: ED[keyof ED]['Operation']; }> | undefined; diff --git a/lib/features/runningTree.js b/lib/features/runningTree.js index 1ef20c7d..4a886cb9 100644 --- a/lib/features/runningTree.js +++ b/lib/features/runningTree.js @@ -830,14 +830,14 @@ class ListNode extends EntityNode { ids.forEach((id) => this.updateItemInner(lsn, data, id, action)); this.setDirty(); } - composeOperations() { + composeOperations(includeModiBranch) { if (!this.dirty) { return; } const intrinsticFilter = this.getIntrinsticFilters(); const ulmLsn = this.ulManager.maxLsn; for (const id in this.children) { - const childOperations = this.children[id].composeOperations(true); + const childOperations = this.children[id].composeOperations(includeModiBranch); if (childOperations) { childOperations.forEach((childOperation) => this.ulManager.push(ulmLsn + 100, childOperation.operation)); } @@ -1318,13 +1318,22 @@ class SingleNode extends EntityNode { }); super.setDirty(); } - composeOperations(fromParent) { + /** + * + * @param includeModiBranch 如果显式置了boolean,代表只要考虑前项路径或者后项路径 + * @returns + */ + composeOperations(includeModiBranch) { if (this.dirty) { const lsnMax = this.ulManager.maxLsn; for (const ele in this.children) { + if (ele.includes(exports.MODI_NEXT_PATH_SUFFIX) && false === includeModiBranch || + (!ele.includes(exports.MODI_NEXT_PATH_SUFFIX) && true === includeModiBranch)) { + continue; + } const ele2 = ele.includes(':') ? ele.slice(0, ele.indexOf(':')) : ele; const child = this.children[ele]; - const childOperations = child.composeOperations(true); + const childOperations = child.composeOperations(includeModiBranch); if (childOperations) { if (child instanceof SingleNode) { (0, assert_1.assert)(childOperations.length === 1); @@ -1683,14 +1692,18 @@ class VirtualNode extends Node { throw err; } } - composeOperations() { + composeOperations(includeModiBranch) { /** * 当一个virtualNode有多个子结点,而这些子结点的前缀一致时,标识这些子结点其实是指向同一个对象,此时需要合并 */ const operationss = []; const operationDict = {}; for (const ele in this.children) { - const operation = this.children[ele].composeOperations(true); + if (ele.includes(exports.MODI_NEXT_PATH_SUFFIX) && false === includeModiBranch || + (!ele.includes(exports.MODI_NEXT_PATH_SUFFIX) && true === includeModiBranch)) { + continue; + } + const operation = this.children[ele].composeOperations(includeModiBranch); if (operation) { const idx = ele.indexOf(':') !== -1 ? ele.slice(0, ele.indexOf(':')) : ele; if (operationDict[idx]) { @@ -1931,7 +1944,7 @@ class RunningTree extends Feature_1.Feature { redoBranchOperations(path, allowInTxn) { const { root } = analyzePath(path); const rootNode = this.root[root]; - const opers = rootNode.composeOperations(); + const opers = rootNode.composeOperations(path.includes(exports.MODI_NEXT_PATH_SUFFIX)); if (opers) { this.cache.redoOperation(opers); } diff --git a/lib/types/Page.d.ts b/lib/types/Page.d.ts index 14d7d46f..cc293a2f 100644 --- a/lib/types/Page.d.ts +++ b/lib/types/Page.d.ts @@ -37,32 +37,63 @@ type FeatureDef = ED[T]['CreateSingle']['data'] | (() => ED[T]['CreateSingle']['data']); interface ComponentOption, FrontCxt extends SyncContext, AD extends Record>, FD extends Record, FormedData extends Record, TData extends DataOption, TProperty extends DataOption, TMethod extends Record, EMethod extends Record = {}> { + /** + * 是否为列表组件 + */ isList?: IsList; zombie?: true; getTotal?: { max: number; deviceWidth?: DevideWidth | 'all'; } | number; + /** + * 组件所关联的实体 + */ entity?: T | ((this: ComponentPublicThisType) => T); path?: string; + /** + * 需要监听的features + */ features?: FeatureDef[]; cascadeActions?: (this: ComponentPublicThisType) => { [K in keyof ED[T]['Schema']]?: ActionDef[]; }; + /** + * 需要校验的actions + */ actions?: ActionDef[] | ((this: ComponentPublicThisType) => ActionDef[]); + /** + * 需要获取的数据字段 + * 若嵌套在list中,需要保证父list组件能够覆盖子组件的projection + */ projection?: ED[T]['Selection']['data'] | ((this: ComponentPublicThisType) => ED[T]['Selection']['data'] | undefined); append?: boolean; + /** + * 分页信息 + */ pagination?: Pick | Array & { deviceWidth: DevideWidth; }>; + /** + * 过滤器 + */ filters?: Array<{ filter: NonNullable | ((this: ComponentPublicThisType) => ED[T]['Selection']['filter'] | undefined); '#name'?: string; }>; + /** + * 排序器 + */ sorters?: Array<{ sorter: NonNullable[number] | ((this: ComponentPublicThisType) => ED[T]['Selection']['sorter'] | NonNullable[number]); '#name'?: string; }>; + /** + * 表单数据 + * 在组件渲染前会调用此方法,将返回的数据放入组件的data中 + * @param options 组件获取到的数据 + * @returns 返回给页面的数据 + */ formData?: (options: { data: IsList extends true ? RowWithActions[] : RowWithActions; origin?: IsList extends true ? RowWithActions[] : RowWithActions; @@ -74,8 +105,17 @@ interface ComponentOption FormedData; ns?: T | T[]; + /** + * 声明的State数据 + */ data?: ((this: ComponentPublicThisType) => TData) | TData; + /** + * 需要参数传入的默认prop参数 + */ properties?: TProperty; + /** + * 自定义方法 + */ methods?: TMethod; } export type MiniprogramStyleMethods = { @@ -106,17 +146,63 @@ export type ComponentFullThisType & OakListComponentMethods & OakSingleComponentMethods; export type OakComponentOption, FrontCxt extends SyncContext, AD extends Record>>, FD extends Record, FormedData extends Record, TData extends Record, TProperty extends DataOption, TMethod extends Record, EMethod extends Record = {}> = ComponentOption & Partial<{ + /** + * 生命周期回调 + */ lifetimes: { + /** + * 在组件实例刚刚被创建时执行 + * sync only + */ created?(): void; + /** + * 在组件实例进入页面节点树时执行 + * sync only + */ attached?(): void; + /** + * 在组件实例进入页面节点树时执行 + * async or sync + */ ready?(): any; + /** + * 小程序专用,在组件实例被移动到节点树另一个位置时执行 + * sync only + */ moved?(): void; + /** + * 在组件实例被从页面节点树移除时执行 + * sync only + */ detached?(): void; + /** + * 小程序专用,每当组件方法抛出错误时执行 + * sync only + * @param err 错误信息 + */ error?(err: Error): void; + /** + * 组件所在的页面被展示时执行 + * sync only + */ show?(): void; + /** + * 组件所在的页面被隐藏时执行 + * sync only + */ hide?(): void; + /** + * 组件所在的页面尺寸变化时执行 + * sync only + * @param size 页面尺寸信息 + */ resize?(size: WechatMiniprogram.Page.IResizeOption): void; }; + /** + * 监听器,监听State或者Props的变化 + * 名称:监听的属性名 + * 回调:监听的回调函数 (prev, next) => void + */ listeners: Record, prev: Record, next: Record) => void>; }> & Partial<{ wechatMp: { diff --git a/package.json b/package.json index 8dceb048..1bf9d36c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oak-frontend-base", - "version": "5.3.19", + "version": "5.3.20", "description": "oak框架中前端与业务逻辑无关的平台部分", "author": { "name": "XuChang" @@ -23,7 +23,7 @@ "node-schedule": "^2.1.1", "nprogress": "^0.2.0", "oak-common-aspect": "^3.0.4", - "oak-domain": "^5.1.10", + "oak-domain": "^5.1.11", "oak-memory-tree-store": "^3.3.6", "ol": "^7.3.0", "rc-pagination": "^4.3.0", diff --git a/src/features/geo.ts b/src/features/geo.ts index e7fedc7d..abb26bc5 100644 --- a/src/features/geo.ts +++ b/src/features/geo.ts @@ -15,12 +15,14 @@ export class Geo< this.cache = cache; } - async searchPoi(name: string, areaId?: string, typeCode?: string) { + async searchPoi(name: string, areaId?: string, typeCode?: string, indexFrom?: number, count?: number) { const { result } = await this.cache.exec('geoService', { api: 'geo', params: { name, - areaId + areaId, + indexFrom, + count, } }); diff --git a/src/features/runningTree.ts b/src/features/runningTree.ts index 01e5ba99..55f7c53e 100644 --- a/src/features/runningTree.ts +++ b/src/features/runningTree.ts @@ -409,7 +409,7 @@ class ListNode< private filters: (NamedFilterItem & { applied?: boolean })[]; private sorters: (NamedSorterItem & { applied?: boolean })[]; private getTotal?: number; - private pagination: Pagination = { ...DEFAULT_PAGINATION}; + private pagination: Pagination = { ...DEFAULT_PAGINATION }; private sr: Record = {}; private syncHandler: (records: OpRecord[]) => void; @@ -1036,7 +1036,7 @@ class ListNode< this.setDirty(); } - composeOperations(): + composeOperations(includeModiBranch?: boolean): | Array<{ entity: keyof ED; operation: ED[keyof ED]['Operation'] }> | undefined { if (!this.dirty) { @@ -1047,7 +1047,7 @@ class ListNode< const ulmLsn = this.ulManager.maxLsn; for (const id in this.children) { - const childOperations = this.children[id].composeOperations(true); + const childOperations = this.children[id].composeOperations(includeModiBranch); if (childOperations) { childOperations.forEach( (childOperation) => this.ulManager.push(ulmLsn + 100, childOperation.operation as any) @@ -1614,16 +1614,25 @@ class SingleNode | undefined { if (this.dirty) { const lsnMax = this.ulManager.maxLsn; for (const ele in this.children) { + if (ele.includes(MODI_NEXT_PATH_SUFFIX) && false === includeModiBranch || + (!ele.includes(MODI_NEXT_PATH_SUFFIX) && true === includeModiBranch)) { + continue; + } const ele2 = ele.includes(':') ? ele.slice(0, ele.indexOf(':')) : ele; const child = this.children[ele]; - const childOperations = child!.composeOperations(true); + const childOperations = child!.composeOperations(includeModiBranch); if (childOperations) { if (child instanceof SingleNode) { assert(childOperations.length === 1); @@ -2015,14 +2024,18 @@ class VirtualNode extends Node { throw err; } } - composeOperations(): Array<{ entity: keyof ED, operation: ED[keyof ED]['Operation'] }> | undefined { + composeOperations(includeModiBranch?: boolean): Array<{ entity: keyof ED, operation: ED[keyof ED]['Operation'] }> | undefined { /** * 当一个virtualNode有多个子结点,而这些子结点的前缀一致时,标识这些子结点其实是指向同一个对象,此时需要合并 */ const operationss = []; const operationDict: Record = {}; for (const ele in this.children) { - const operation = this.children[ele].composeOperations(true); + if (ele.includes(MODI_NEXT_PATH_SUFFIX) && false === includeModiBranch || + (!ele.includes(MODI_NEXT_PATH_SUFFIX) && true === includeModiBranch)) { + continue; + } + const operation = this.children[ele].composeOperations(includeModiBranch); if (operation) { const idx = ele.indexOf(':') !== -1 ? ele.slice(0, ele.indexOf(':')) : ele; if (operationDict[idx]) { @@ -2338,7 +2351,7 @@ export class RunningTree extends Feature const { root } = analyzePath(path); const rootNode = this.root[root]; - const opers = rootNode.composeOperations(); + const opers = rootNode.composeOperations(path.includes(MODI_NEXT_PATH_SUFFIX)); if (opers) { this.cache.redoOperation(opers); } diff --git a/src/types/Page.ts b/src/types/Page.ts index ef8cc3ae..26faf1f7 100644 --- a/src/types/Page.ts +++ b/src/types/Page.ts @@ -137,31 +137,62 @@ interface ComponentOption< TMethod extends Record, EMethod extends Record = {}, > { + /** + * 是否为列表组件 + */ isList?: IsList; zombie?: true; getTotal?: { max: number; deviceWidth?: DevideWidth | 'all'; } | number; + /** + * 组件所关联的实体 + */ entity?: T | ((this: ComponentPublicThisType) => T); path?: string; + /** + * 需要监听的features + */ features?: FeatureDef[]; cascadeActions?: (this: ComponentPublicThisType) => { [K in keyof ED[T]['Schema']]?: ActionDef[]; }, + /** + * 需要校验的actions + */ actions?: ActionDef[] | ((this: ComponentPublicThisType) => ActionDef[]); + /** + * 需要获取的数据字段 + * 若嵌套在list中,需要保证父list组件能够覆盖子组件的projection + */ projection?: ED[T]['Selection']['data'] | ((this: ComponentPublicThisType) => ED[T]['Selection']['data'] | undefined); append?: boolean; + /** + * 分页信息 + */ pagination?: Pick | Array & { deviceWidth: DevideWidth }>; + /** + * 过滤器 + */ filters?: Array<{ filter: NonNullable | ((this: ComponentPublicThisType) => ED[T]['Selection']['filter'] | undefined); '#name'?: string; }>; + /** + * 排序器 + */ sorters?: Array<{ sorter: NonNullable[number] | ((this: ComponentPublicThisType) => ED[T]['Selection']['sorter'] | NonNullable[number]); '#name'?: string; }>; + /** + * 表单数据 + * 在组件渲染前会调用此方法,将返回的数据放入组件的data中 + * @param options 组件获取到的数据 + * @returns 返回给页面的数据 + */ formData?: (options: { data: IsList extends true ? RowWithActions[] : RowWithActions; origin?: IsList extends true ? RowWithActions[] : RowWithActions; @@ -173,8 +204,17 @@ interface ComponentOption< modified: boolean; }) => FormedData; ns?: T | T[]; + /** + * 声明的State数据 + */ data?: ((this: ComponentPublicThisType) => TData) | TData; + /** + * 需要参数传入的默认prop参数 + */ properties?: TProperty; + /** + * 自定义方法 + */ methods?: TMethod; // createData?: CreateDataDef; }; @@ -283,17 +323,63 @@ export type OakComponentOption< EMethod extends Record = {}, > = ComponentOption & Partial<{ + /** + * 生命周期回调 + */ lifetimes: { + /** + * 在组件实例刚刚被创建时执行 + * sync only + */ created?(): void; + /** + * 在组件实例进入页面节点树时执行 + * sync only + */ attached?(): void; + /** + * 在组件实例进入页面节点树时执行 + * async or sync + */ ready?(): any; + /** + * 小程序专用,在组件实例被移动到节点树另一个位置时执行 + * sync only + */ moved?(): void; + /** + * 在组件实例被从页面节点树移除时执行 + * sync only + */ detached?(): void; + /** + * 小程序专用,每当组件方法抛出错误时执行 + * sync only + * @param err 错误信息 + */ error?(err: Error): void; + /** + * 组件所在的页面被展示时执行 + * sync only + */ show?(): void; + /** + * 组件所在的页面被隐藏时执行 + * sync only + */ hide?(): void; + /** + * 组件所在的页面尺寸变化时执行 + * sync only + * @param size 页面尺寸信息 + */ resize?(size: WechatMiniprogram.Page.IResizeOption): void; }; + /** + * 监听器,监听State或者Props的变化 + * 名称:监听的属性名 + * 回调:监听的回调函数 (prev, next) => void + */ listeners: Record, prev: Record, next: Record) => void>;