diff --git a/lib/features/runningTree.js b/lib/features/runningTree.js index a696dda6..51ed4eb5 100644 --- a/lib/features/runningTree.js +++ b/lib/features/runningTree.js @@ -1785,10 +1785,10 @@ var VirtualNode = /** @class */ (function (_super) { if (child instanceof SingleNode || child instanceof ListNode) { var entity = child.getEntity(); if (child instanceof SingleNode) { - (0, assert_1.assert)(path === entity || path.startsWith("".concat(entity, ":")), "oakPath\u300C".concat(path, "\u300D\u4E0D\u7B26\u5408\u547D\u540D\u89C4\u8303\uFF0C\u8BF7\u4EE5\u300C").concat(entity, "\u300D\u4E3A\u547D\u540D\u8D77\u59CB\u6807\u8BC6")); + (0, assert_1.assert)(path === entity || path.startsWith("".concat(entity, ":")), "oakPath\u300C".concat(path, "\u300D\u4E0D\u7B26\u5408\u547D\u540D\u89C4\u8303\uFF0C\u8BF7\u4EE5\u300C").concat(entity, ":\u300D\u4E3A\u547D\u540D\u8D77\u59CB\u6807\u8BC6")); } else { - (0, assert_1.assert)(path === "".concat(entity, "s") || path.startsWith("".concat(entity, "s:")), "oakPath\u300C".concat(path, "\u300D\u4E0D\u7B26\u5408\u547D\u540D\u89C4\u8303\uFF0C\u8BF7\u4EE5\u300C").concat(entity, "s\u300D\u4E3A\u547D\u540D\u8D77\u59CB\u6807\u8BC6")); + (0, assert_1.assert)(path === "".concat(entity, "s") || path.startsWith("".concat(entity, "s:")), "oakPath\u300C".concat(path, "\u300D\u4E0D\u7B26\u5408\u547D\u540D\u89C4\u8303\uFF0C\u8BF7\u4EE5\u300C").concat(entity, "s:\u300D\u4E3A\u547D\u540D\u8D77\u59CB\u6807\u8BC6")); } } }; diff --git a/lib/page.mp.js b/lib/page.mp.js index 6ed33d35..1dcfd917 100644 --- a/lib/page.mp.js +++ b/lib/page.mp.js @@ -505,6 +505,9 @@ function createComponent(option, features) { if (option.entity) { this.subscribed.push(features.cache.subscribe(function () { return _this.reRender(); })); } + if (option.features) { + option.features.forEach(function (ele) { return _this.subscribed.push(features[ele].subscribe(function () { return _this.reRender(); })); }); + } attached && attached.call(this); }, detached: function () { diff --git a/lib/page.web.js b/lib/page.web.js index 352bef6e..1619338f 100644 --- a/lib/page.web.js +++ b/lib/page.web.js @@ -641,7 +641,11 @@ function createComponent(option, features) { } this.reRender(); _a.label = 3; - case 3: return [2 /*return*/]; + case 3: + if (option.features) { + option.features.forEach(function (ele) { return _this.subscribed.push(features[ele].subscribe(function () { return _this.reRender(); })); }); + } + return [2 /*return*/]; } }); }); diff --git a/lib/types/Page.d.ts b/lib/types/Page.d.ts index 0c3d15e4..31122ba4 100644 --- a/lib/types/Page.d.ts +++ b/lib/types/Page.d.ts @@ -38,10 +38,26 @@ declare type FullPropertyToData = ValueType = { [name in keyof P]: PropertyToData; }; +declare type CascadeEntity = { + aggr?: ED[T]['Aggregation']; + selection?: ED[T]['Selection']; + actions?: [ + { + action: ED[T]['Action']; + filters?: Array<{ + filter: ED[T]['Selection']['filter']; + '#name'?: string; + }>; + data?: Partial; + } + ]; +}; interface ComponentOption, FrontCxt extends SyncContext, AD extends Record>, FD extends Record, FormedData extends Record, IsList extends boolean, TData extends DataOption, TProperty extends PropertyOption> { entity?: T | ((this: ComponentPublicThisType) => T); path?: string; isList: IsList; + features?: (keyof (FD & BasicFeatures>))[]; + cascadeEntities?: (this: ComponentPublicThisType) => Record>; projection?: ED[T]['Selection']['data'] | ((this: ComponentPublicThisType) => ED[T]['Selection']['data']); append?: boolean; pagination?: Pagination; diff --git a/src/features/runningTree.ts b/src/features/runningTree.ts index a30139d9..6425aeb5 100644 --- a/src/features/runningTree.ts +++ b/src/features/runningTree.ts @@ -1777,10 +1777,10 @@ class VirtualNode< if (child instanceof SingleNode || child instanceof ListNode) { const entity = child.getEntity() as string; if (child instanceof SingleNode) { - assert(path === entity || path.startsWith(`${entity}:`), `oakPath「${path}」不符合命名规范,请以「${entity}」为命名起始标识`); + assert(path === entity || path.startsWith(`${entity}:`), `oakPath「${path}」不符合命名规范,请以「${entity}:」为命名起始标识`); } else { - assert(path === `${entity}s` || path.startsWith(`${entity}s:`), `oakPath「${path}」不符合命名规范,请以「${entity}s」为命名起始标识`); + assert(path === `${entity}s` || path.startsWith(`${entity}s:`), `oakPath「${path}」不符合命名规范,请以「${entity}s:」为命名起始标识`); } } } diff --git a/src/page.mp.ts b/src/page.mp.ts index 4046f182..e6f41311 100644 --- a/src/page.mp.ts +++ b/src/page.mp.ts @@ -761,6 +761,15 @@ export function createComponent< features.cache.subscribe(() => this.reRender()) ); } + if (option.features) { + option.features.forEach( + ele => this.subscribed.push( + features[ele].subscribe( + () => this.reRender() + ) + ) + ); + } attached && attached.call(this); }, detached() { diff --git a/src/page.web.tsx b/src/page.web.tsx index 9a7896a3..8ccd5c2e 100644 --- a/src/page.web.tsx +++ b/src/page.web.tsx @@ -906,6 +906,15 @@ export function createComponent< } this.reRender(); } + if (option.features) { + option.features.forEach( + ele => this.subscribed.push( + features[ele].subscribe( + () => this.reRender() + ) + ) + ); + } } componentWillUnmount() { diff --git a/src/types/Page.ts b/src/types/Page.ts index f68746a5..aa2915c0 100644 --- a/src/types/Page.ts +++ b/src/types/Page.ts @@ -41,7 +41,7 @@ type ValueType = T extends null ? AnyObject : T extends FunctionConstructor ? Function - :never + : never type FullProperty = { /** 属性类型 */ type: T @@ -82,6 +82,21 @@ type PropertyOptionToData

= { [name in keyof P]: PropertyToData }; +type CascadeEntity< + ED extends EntityDict & BaseEntityDict, + T extends keyof ED> = { + aggr?: ED[T]['Aggregation'], + selection?: ED[T]['Selection'], + actions?: [{ + action: ED[T]['Action'], + filters?: Array<{ + filter: ED[T]['Selection']['filter']; + '#name'?: string; + }>; + data?: Partial; + }]; + }; + interface ComponentOption< ED extends EntityDict & BaseEntityDict, T extends keyof ED, @@ -93,10 +108,12 @@ interface ComponentOption< IsList extends boolean, TData extends DataOption, TProperty extends PropertyOption, - > { +> { entity?: T | ((this: ComponentPublicThisType) => T); path?: string; isList: IsList; + features?: (keyof (FD & BasicFeatures>))[]; + cascadeEntities?: (this: ComponentPublicThisType) => Record>, projection?: ED[T]['Selection']['data'] | ((this: ComponentPublicThisType) => ED[T]['Selection']['data']); append?: boolean; pagination?: Pagination; @@ -147,7 +164,7 @@ export type ComponentData< T extends keyof ED, FormedData extends DataOption, TData extends DataOption - > = TData & FormedData & OakComponentData; +> = TData & FormedData & OakComponentData; export type ComponentPublicThisType< ED extends EntityDict & BaseEntityDict, @@ -162,21 +179,21 @@ export type ComponentPublicThisType< TProperty extends PropertyOption = {}, TMethod extends MethodOption = {}, EMethod extends Record = {} - > = { - subscribed: Array<() => void>; - features: FD & BasicFeatures>; - state: ComponentData; - props: Readonly>; - setState: ( - data: Partial>, - callback?: () => void, - ) => void; - triggerEvent: ( - name: string, - detail?: DetailType, - options?: WechatMiniprogram.Component.TriggerEventOption - ) => void; - } & TMethod & EMethod & OakCommonComponentMethods & OakListComponentMethods & OakSingleComponentMethods; +> = { + subscribed: Array<() => void>; + features: FD & BasicFeatures>; + state: ComponentData; + props: Readonly>; + setState: ( + data: Partial>, + callback?: () => void, + ) => void; + triggerEvent: ( + name: string, + detail?: DetailType, + options?: WechatMiniprogram.Component.TriggerEventOption + ) => void; +} & TMethod & EMethod & OakCommonComponentMethods & OakListComponentMethods & OakSingleComponentMethods; export type ComponentFullThisType< ED extends EntityDict & BaseEntityDict, @@ -184,21 +201,21 @@ export type ComponentFullThisType< IsList extends boolean, Cxt extends AsyncContext, FrontCxt extends SyncContext - > = { - subscribed: Array<() => void>; - features: BasicFeatures>; - state: OakComponentData; - props: ComponentProps; - setState: ( - data: Partial>, - callback?: () => void, - ) => void; - triggerEvent: ( - name: string, - detail?: DetailType, - options?: WechatMiniprogram.Component.TriggerEventOption - ) => void; - } & OakCommonComponentMethods & OakListComponentMethods & OakSingleComponentMethods; +> = { + subscribed: Array<() => void>; + features: BasicFeatures>; + state: OakComponentData; + props: ComponentProps; + setState: ( + data: Partial>, + callback?: () => void, + ) => void; + triggerEvent: ( + name: string, + detail?: DetailType, + options?: WechatMiniprogram.Component.TriggerEventOption + ) => void; +} & OakCommonComponentMethods & OakListComponentMethods & OakSingleComponentMethods; export type OakComponentOption< ED extends EntityDict & BaseEntityDict, @@ -213,7 +230,7 @@ export type OakComponentOption< TProperty extends PropertyOption, TMethod extends Record, EMethod extends Record = {} - > = ComponentOption & +> = ComponentOption & Partial<{ methods: TMethod; lifetimes: { @@ -273,60 +290,60 @@ export type OakNavigateToParameters = { - setDisablePulldownRefresh: (able: boolean) => void; - sub: (type: string, callback: Function) => void; - unsub: (type: string, callback: Function) => void; - pub: (type: string, options?: any) => void; - unsubAll: (type: string) => void; - save: (key: string, item: any) => void; - load: (key: string) => any; - clear: () => void; - resolveInput: ( - input: any, - keys?: K[] - ) => { dataset?: Record; value?: string } & { - [k in K]?: any; - }; - setNotification: (data: NotificationProps) => void; - consumeNotification: () => NotificationProps | undefined; - setMessage: (data: MessageProps) => void; - consumeMessage: () => MessageProps | undefined; - reRender: (extra?: Record) => void; - getFreshValue: (path?: string) => - | Partial[] - | Partial - | undefined; - navigateTo: ( - options: { url: string } & OakNavigateToParameters, - state?: Record, - disableNamespace?: boolean - ) => Promise; - navigateBack: (delta?: number) => Promise; - redirectTo: ( - options: Parameters[0] & - OakNavigateToParameters, - state?: Record, - disableNamespace?: boolean - ) => Promise; - // setProps: (props: Record, usingState?: true) => void; - clean: (path?: string) => void; - - t(key: string, params?: object): string; - execute: (action?: ED[T]['Action'], messageProps?: boolean | MessageProps) => Promise; - checkOperation: ( - ntity: T, - action: ED[T]['Action'], - filter?: ED[T]['Update']['filter'], - checkerTypes?: CheckerType[] - ) => boolean; - tryExecute: (path?: string) => boolean | Error; - getOperations: ( - path?: string - ) => { operation: ED[T]['Operation']; entity: T }[] | undefined; - refresh: () => Promise; - aggregate: (aggregation: ED[T]['Aggregation']) => Promise>; +> = { + setDisablePulldownRefresh: (able: boolean) => void; + sub: (type: string, callback: Function) => void; + unsub: (type: string, callback: Function) => void; + pub: (type: string, options?: any) => void; + unsubAll: (type: string) => void; + save: (key: string, item: any) => void; + load: (key: string) => any; + clear: () => void; + resolveInput: ( + input: any, + keys?: K[] + ) => { dataset?: Record; value?: string } & { + [k in K]?: any; }; + setNotification: (data: NotificationProps) => void; + consumeNotification: () => NotificationProps | undefined; + setMessage: (data: MessageProps) => void; + consumeMessage: () => MessageProps | undefined; + reRender: (extra?: Record) => void; + getFreshValue: (path?: string) => + | Partial[] + | Partial + | undefined; + navigateTo: ( + options: { url: string } & OakNavigateToParameters, + state?: Record, + disableNamespace?: boolean + ) => Promise; + navigateBack: (delta?: number) => Promise; + redirectTo: ( + options: Parameters[0] & + OakNavigateToParameters, + state?: Record, + disableNamespace?: boolean + ) => Promise; + // setProps: (props: Record, usingState?: true) => void; + clean: (path?: string) => void; + + t(key: string, params?: object): string; + execute: (action?: ED[T]['Action'], messageProps?: boolean | MessageProps) => Promise; + checkOperation: ( + ntity: T, + action: ED[T]['Action'], + filter?: ED[T]['Update']['filter'], + checkerTypes?: CheckerType[] + ) => boolean; + tryExecute: (path?: string) => boolean | Error; + getOperations: ( + path?: string + ) => { operation: ED[T]['Operation']; entity: T }[] | undefined; + refresh: () => Promise; + aggregate: (aggregation: ED[T]['Aggregation']) => Promise>; +}; export type OakSingleComponentMethods = { setId: (id: string) => void; @@ -377,31 +394,31 @@ export type OakComponentOnlyMethods = { export type OakComponentData< ED extends EntityDict & BaseEntityDict, T extends keyof ED - > = { - oakExecutable: boolean | Error; - oakExecuting: boolean; - oakFocused: { - attr: string; - message: string; - }; - oakDirty: boolean; - oakLoading: boolean; - oakLoadingMore: boolean; - oakPullDownRefreshLoading: boolean; - oakEntity: T; - oakFullpath: string; - oakLegalActions?: ED[T]['Action'][]; - oakDisablePulldownRefresh: boolean; +> = { + oakExecutable: boolean | Error; + oakExecuting: boolean; + oakFocused: { + attr: string; + message: string; }; + oakDirty: boolean; + oakLoading: boolean; + oakLoadingMore: boolean; + oakPullDownRefreshLoading: boolean; + oakEntity: T; + oakFullpath: string; + oakLegalActions?: ED[T]['Action'][]; + oakDisablePulldownRefresh: boolean; +}; export type OakListComoponetData< ED extends EntityDict & BaseEntityDict, T extends keyof ED - > = { - oakFilters?: NonNullable[]; - oakSorters?: NonNullable[]; - oakPagination?: Pagination; - } +> = { + oakFilters?: NonNullable[]; + oakSorters?: NonNullable[]; + oakPagination?: Pagination; +} export type MakeOakComponent< ED extends EntityDict & BaseEntityDict, @@ -409,28 +426,28 @@ export type MakeOakComponent< FrontCxt extends SyncContext, AD extends Record>, FD extends Record - > = < - T extends keyof ED, - FormedData extends DataOption, - IsList extends boolean, - TData extends DataOption, - TProperty extends PropertyOption, - TMethod extends MethodOption - >( - options: OakComponentOption< - ED, - T, - Cxt, - FrontCxt, - AD, - FD, - FormedData, - IsList, - TData, - TProperty, - TMethod - > - ) => React.ComponentType; +> = < + T extends keyof ED, + FormedData extends DataOption, + IsList extends boolean, + TData extends DataOption, + TProperty extends PropertyOption, + TMethod extends MethodOption +>( + options: OakComponentOption< + ED, + T, + Cxt, + FrontCxt, + AD, + FD, + FormedData, + IsList, + TData, + TProperty, + TMethod + > +) => React.ComponentType; // 暴露给组件的方法 export type WebComponentCommonMethodNames = 'setNotification' | 'setMessage' | 'navigateTo' | 'navigateBack' | 'redirectTo' | 'clean' | 't' | 'execute' | 'refresh' | 'setDisablePulldownRefresh' | 'aggregate' | 'checkOperation';