From b66d409e1fc6448e18d4ad771f89c34417f098e9 Mon Sep 17 00:00:00 2001 From: "Xc@centOs" Date: Mon, 7 Nov 2022 22:41:41 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=80=E4=BA=9B=E5=AE=9A=E4=B9=89=E4=B8=8A?= =?UTF-8?q?=E7=9A=84=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/cacheStore/CacheStore.d.ts | 7 +- lib/cacheStore/CacheStore.js | 26 +- lib/debugStore/DebugStore.d.ts | 1 - lib/debugStore/DebugStore.js | 20 - lib/features/runningTree.d.ts | 69 +- lib/features/runningTree.js | 1109 ++++++++++++++++++-------------- lib/index.d.ts | 1 + lib/index.js | 1 + lib/page.common.d.ts | 22 +- lib/page.common.js | 98 +-- lib/page.web.js | 166 ++++- lib/types/Page.d.ts | 32 +- src/cacheStore/CacheStore.ts | 26 +- src/debugStore/DebugStore.ts | 20 - src/features/runningTree.ts | 519 ++++++++------- src/index.ts | 3 +- src/page.common.ts | 116 +--- src/page.web.tsx | 188 +++++- src/types/Page.ts | 82 ++- 19 files changed, 1366 insertions(+), 1140 deletions(-) diff --git a/lib/cacheStore/CacheStore.d.ts b/lib/cacheStore/CacheStore.d.ts index 31cda46c..bcc0b1f0 100644 --- a/lib/cacheStore/CacheStore.d.ts +++ b/lib/cacheStore/CacheStore.d.ts @@ -1,17 +1,13 @@ -import { DeduceCreateSingleOperation, DeduceRemoveOperation, DeduceUpdateOperation, EntityDict, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity'; +import { EntityDict, OperationResult, OpRecord, SelectOption } from 'oak-domain/lib/types/Entity'; import { StorageSchema } from "oak-domain/lib/types/Storage"; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; import { Checker, CheckerType, Context } from 'oak-domain/lib/types'; import { TreeStore, TreeStoreOperateOption } from 'oak-memory-tree-store'; -interface CachStoreOperation extends TreeStoreOperateOption { - inSync?: boolean; -} export declare class CacheStore> extends TreeStore { private executor; private getFullDataFn?; private resetInitialDataFn?; constructor(storageSchema: StorageSchema, contextBuilder: () => (store: CacheStore) => Cxt, getFullDataFn?: () => any, resetInitialDataFn?: () => void); - protected updateAbjointRow(entity: T, operation: DeduceCreateSingleOperation | DeduceUpdateOperation | DeduceRemoveOperation, context: Cxt, option?: CachStoreOperation): Promise; operate(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): Promise>; sync(opRecords: Array>, context: Cxt): Promise; check(entity: T, operation: ED[T]['Operation'], context: Cxt, checkerTypes?: CheckerType[]): Promise; @@ -28,4 +24,3 @@ export declare class CacheStore, contextBuilder: (cxtString?: string) => (store: RowStore) => Promise); - protected updateAbjointRow(entity: T, operation: ED[T]['CreateSingle'] | ED[T]['Update'] | ED[T]['Remove'], context: Cxt, option?: OP): Promise; protected cascadeUpdate(entity: T, operation: DeduceCreateOperation | DeduceUpdateOperation | DeduceRemoveOperation, context: Cxt, option: OP): Promise>; protected cascadeSelect(entity: T, selection: S, context: Cxt, option: OP): Promise[]>; operate(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): Promise>; diff --git a/lib/debugStore/DebugStore.js b/lib/debugStore/DebugStore.js index 2606145c..8a3bbcbd 100644 --- a/lib/debugStore/DebugStore.js +++ b/lib/debugStore/DebugStore.js @@ -15,26 +15,6 @@ var DebugStore = /** @class */ (function (_super) { _this.rwLock = new concurrent_1.RWLock(); return _this; } - DebugStore.prototype.updateAbjointRow = function (entity, operation, context, option) { - return tslib_1.__awaiter(this, void 0, void 0, function () { - var action, data, attributes, key; - var _a; - return tslib_1.__generator(this, function (_b) { - action = operation.action, data = operation.data; - if (action === 'create') { - attributes = this.getSchema()[entity].attributes; - for (key in attributes) { - if (data[key] === undefined) { - Object.assign(data, (_a = {}, - _a[key] = null, - _a)); - } - } - } - return [2 /*return*/, _super.prototype.updateAbjointRow.call(this, entity, operation, context, option)]; - }); - }); - }; DebugStore.prototype.cascadeUpdate = function (entity, operation, context, option) { return tslib_1.__awaiter(this, void 0, void 0, function () { var result; diff --git a/lib/features/runningTree.d.ts b/lib/features/runningTree.d.ts index 88e184ee..5a429ad2 100644 --- a/lib/features/runningTree.d.ts +++ b/lib/features/runningTree.d.ts @@ -5,24 +5,18 @@ import { NamedFilterItem, NamedSorterItem } from "../types/NamedCondition"; import { Cache } from './cache'; import { Pagination } from '../types/Pagination'; import { Feature } from '../types/Feature'; -declare type Operation = { - oper: OmitId extends true ? Omit : ED[T]['Operation']; - beforeExecute?: () => Promise; - afterExecute?: () => Promise; -}; declare abstract class Node, AD extends CommonAspectDict> { protected entity: T; protected schema: StorageSchema; protected projection: ED[T]['Selection']['data'] | (() => Promise); - protected parent?: Node | VirtualNode; + protected parent?: SingleNode | ListNode | VirtualNode; protected dirty?: boolean; protected cache: Cache; protected loading: boolean; protected loadingMore: boolean; protected executing: boolean; - protected operations: Operation[]; protected modiIds: string[] | undefined; - constructor(entity: T, schema: StorageSchema, cache: Cache, projection: ED[T]['Selection']['data'] | (() => Promise), parent?: Node | VirtualNode); + constructor(entity: T, schema: StorageSchema, cache: Cache, projection: ED[T]['Selection']['data'] | (() => Promise), parent?: SingleNode | ListNode | VirtualNode); getEntity(): T; getSchema(): StorageSchema; protected abstract getChildPath(child: Node): string; @@ -40,7 +34,7 @@ declare abstract class Node | VirtualNode | undefined; + getParent(): SingleNode | ListNode | VirtualNode | undefined; protected getProjection(): Promise; protected judgeRelation(attr: string): string | 0 | 1 | 2 | string[]; protected contains(filter: ED[T]['Selection']['filter'], conditionalFilter: ED[T]['Selection']['filter']): boolean; @@ -48,6 +42,7 @@ declare abstract class Node, AD extends CommonAspectDict> extends Node { private children; + private updates; private filters; private sorters; private pagination; @@ -58,7 +53,7 @@ declare class ListNode[]): Promise; destroy(): void; - constructor(entity: T, schema: StorageSchema, cache: Cache, projection: ED[T]['Selection']['data'] | (() => Promise), parent?: Node | VirtualNode, filters?: NamedFilterItem[], sorters?: NamedSorterItem[], pagination?: Pagination); + constructor(entity: T, schema: StorageSchema, cache: Cache, projection: ED[T]['Selection']['data'] | (() => Promise), parent?: SingleNode | VirtualNode, filters?: NamedFilterItem[], sorters?: NamedSorterItem[], pagination?: Pagination); getPagination(): Pagination; setPagination(pagination: Pagination): Promise; getChild(path: string): SingleNode | undefined; @@ -78,13 +73,22 @@ declare class ListNode, refresh?: boolean): Promise; removeNamedSorterByName(name: string, refresh: boolean): Promise; getFreshValue(): Promise>>; - addOperation(oper: Omit, beforeExecute?: Operation['beforeExecute'], afterExecute?: Operation['afterExecute']): Promise; + addItem(item: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise): Promise; + removeItem(id: string, beforeExecute?: () => Promise, afterExecute?: () => Promise): Promise; + recoverItem(id: string): Promise; + /** + * 目前只支持根据itemId进行更新 + * @param data + * @param id + * @param beforeExecute + * @param afterExecute + */ + updateItem(data: ED[T]['Update']['data'], id: string, action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise): Promise; + updateItems(data: Record, action?: ED[T]['Action']): Promise; doBeforeTrigger(): Promise; doAfterTrigger(): Promise; getParentFilter(childNode: SingleNode): Promise; - composeOperations(): Promise<(ED[T]["Operation"] & { - entity: T; - })[] | undefined>; + composeOperations(): Promise; getProjection(): Promise; constructSelection(withParent?: true, disableOperation?: boolean): Promise<{ data: ED[T]["Selection"]["data"]; @@ -100,7 +104,9 @@ declare class ListNode, AD extends CommonAspectDict> extends Node { private id?; private children; - constructor(entity: T, schema: StorageSchema, cache: Cache, projection: ED[T]['Selection']['data'] | (() => Promise), parent?: Node | VirtualNode, id?: string); + private operation?; + constructor(entity: T, schema: StorageSchema, cache: Cache, projection: ED[T]['Selection']['data'] | (() => Promise), parent?: SingleNode | ListNode | VirtualNode, id?: string); + private tryGetParentFilter; protected getChildPath(child: Node): string; setLoading(loading: boolean): void; checkIfClean(): void; @@ -117,11 +123,14 @@ declare class SingleNode | undefined>; doBeforeTrigger(): Promise; doAfterTrigger(): Promise; - addOperation(oper: Omit, beforeExecute?: Operation['beforeExecute'], afterExecute?: Operation['afterExecute']): Promise; - composeOperations(): Promise<(ED[T]["Operation"] & { + create(data: Partial>, beforeExecute?: () => Promise, afterExecute?: () => Promise): Promise; + update(data: ED[T]['Update']['data'], action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise): Promise; + remove(beforeExecute?: () => Promise, afterExecute?: () => Promise): Promise; + composeOperations(): Promise; - getProjection(withDecendants?: boolean): Promise; + operation: ED[T]['Update']; + }> | undefined>; + getProjection(withDecendants?: boolean): Promise; refresh(): Promise; clean(): void; getParentFilter(childNode: Node, disableOperation?: boolean): Promise; @@ -133,7 +142,7 @@ declare class VirtualNode { getActiveModies(child: any): Promise; setDirty(): void; addChild(path: string, child: SingleNode | ListNode): void; - getChild(path: string): ListNode | SingleNode | undefined; + getChild(path: string): SingleNode | ListNode | undefined; getParent(): undefined; destroy(): void; getFreshValue(): Promise; @@ -170,13 +179,19 @@ export declare class RunningTree | undefined> | Promise[]> | undefined; isDirty(path: string): boolean; - addOperation(path: string, operation: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise): Promise; + addItem(path: string, data: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise): Promise; + removeItem(path: string, id: string, beforeExecute?: () => Promise, afterExecute?: () => Promise): Promise; + updateItem(path: string, data: ED[T]['Update']['data'], id: string, action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise): Promise; + recoverItem(path: string, id: string): Promise; + create(path: string, data: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise): Promise; + update(path: string, data: ED[T]['Update']['data'], action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise): Promise; + remove(path: string, beforeExecute?: () => Promise, afterExecute?: () => Promise): Promise; isLoading(path: string): boolean; isLoadingMore(path: string): boolean; isExecuting(path: string): boolean; refresh(path: string): Promise; loadMore(path: string): Promise; - getPagination(path: string): Pagination; + getPagination(path: string): Pagination; setId(path: string, id: string): Promise; unsetId(path: string): Promise; getId(path: string): string | undefined; @@ -193,14 +208,10 @@ export declare class RunningTree(path: string, sorters: NamedSorterItem[], refresh?: boolean): Promise; addNamedSorter(path: string, sorter: NamedSorterItem, refresh?: boolean): Promise; removeNamedSorter(path: string, sorter: NamedSorterItem, refresh?: boolean): Promise; - removeNamedSorterByName(path: string, name: string, refresh?: boolean): Promise; + removeNamedSorterByName(path: string, name: string, refresh?: boolean): Promise; tryExecute(path: string): Promise; - getOperations(path: string): Promise; - execute(path: string, operation?: Omit): Promise; + getOperations(path: string): Promise; + execute(path: string, data?: ED[T]['Update']['data'] | Record, action?: ED[T]['Action']): Promise; clean(path: string): Promise; getRoot(): Record | ListNode>; } diff --git a/lib/features/runningTree.js b/lib/features/runningTree.js index 8c9af120..c208c96c 100644 --- a/lib/features/runningTree.js +++ b/lib/features/runningTree.js @@ -19,7 +19,6 @@ var Node = /** @class */ (function () { this.loading = false; this.loadingMore = false; this.executing = false; - this.operations = []; this.modiIds = undefined; } Node.prototype.getEntity = function () { @@ -517,6 +516,7 @@ var ListNode = /** @class */ (function (_super) { _this.filters = filters || []; _this.sorters = sorters || []; _this.pagination = pagination || DEFAULT_PAGINATION; + _this.updates = {}; _this.syncHandler = function (records) { return _this.onCacheSync(records); }; _this.cache.bindOnSync(_this.syncHandler); return _this; @@ -539,7 +539,7 @@ var ListNode = /** @class */ (function (_super) { }; ListNode.prototype.checkIfClean = function () { var _a; - if (this.operations.length > 0) { + if (Object.keys(this.updates).length > 0) { return; } for (var k in this.children) { @@ -835,51 +835,36 @@ var ListNode = /** @class */ (function (_super) { }; ListNode.prototype.getFreshValue = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { - var createdIds, _a, _b, operation, oper, data, modies, _c, operations, selection, filter, result; - var e_10, _d; + var createdIds, k, operation, data, modies, _a, operations, selection, filter, result; var _this = this; - return tslib_1.__generator(this, function (_e) { - switch (_e.label) { + return tslib_1.__generator(this, function (_b) { + switch (_b.label) { case 0: createdIds = []; - try { - for (_a = tslib_1.__values(this.operations), _b = _a.next(); !_b.done; _b = _a.next()) { - operation = _b.value; - oper = operation.oper; - if (oper.action === 'create') { - data = oper.data; - if (data instanceof Array) { - createdIds.push.apply(createdIds, tslib_1.__spreadArray([], tslib_1.__read(data.map(function (ele) { return ele.id; })), false)); - } - else { - createdIds.push(data.id); - } - } + for (k in this.updates) { + operation = this.updates[k].operation; + if (operation.action === 'create') { + data = operation.data; + (0, assert_1.assert)(!(data instanceof Array)); + createdIds.push(data.id); } } - catch (e_10_1) { e_10 = { error: e_10_1 }; } - finally { - try { - if (_b && !_b.done && (_d = _a.return)) _d.call(_a); - } - finally { if (e_10) throw e_10.error; } - } - _c = this.parent; - if (!_c) return [3 /*break*/, 2]; + _a = this.parent; + if (!_a) return [3 /*break*/, 2]; return [4 /*yield*/, this.parent.getActiveModies(this)]; case 1: - _c = (_e.sent()); - _e.label = 2; + _a = (_b.sent()); + _b.label = 2; case 2: - modies = _c; + modies = _a; operations = modies ? (0, modi_1.createOperationsFromModies)(modies) : []; - operations.push.apply(operations, tslib_1.__spreadArray([], tslib_1.__read(this.operations.map(function (ele) { return ({ + operations.push.apply(operations, tslib_1.__spreadArray([], tslib_1.__read(Object.keys(this.updates).map(function (ele) { return ({ entity: _this.entity, - operation: ele.oper, + operation: _this.updates[ele].operation, }); })), false)); return [4 /*yield*/, this.constructSelection(true)]; case 3: - selection = _e.sent(); + selection = _b.sent(); if (!(selection.validParentFilter || createdIds.length > 0)) return [3 /*break*/, 5]; if (undefined === modies) { Object.assign(selection, { @@ -898,144 +883,251 @@ var ListNode = /** @class */ (function (_super) { } return [4 /*yield*/, this.cache.tryRedoOperationsThenSelect(this.entity, selection, operations)]; case 4: - result = _e.sent(); + result = _b.sent(); return [2 /*return*/, result]; case 5: return [2 /*return*/, []]; } }); }); }; - ListNode.prototype.addOperation = function (oper, beforeExecute, afterExecute) { + ListNode.prototype.addItem = function (item, beforeExecute, afterExecute) { return tslib_1.__awaiter(this, void 0, void 0, function () { - var operation, merged, _a, _b, _c; - var _d; + var id, _a, _b; + var _c, _d; return tslib_1.__generator(this, function (_e) { switch (_e.label) { - case 0: - operation = { - oper: oper, + case 0: return [4 /*yield*/, generateNewId()]; + case 1: + id = _e.sent(); + (0, assert_1.assert)(!this.updates[id]); + _a = this.updates; + _b = id; + _c = { beforeExecute: beforeExecute, - afterExecute: afterExecute, + afterExecute: afterExecute }; - merged = tryMergeOperationToExisted(this.entity, this.schema, operation, this.operations); - if (!!merged) return [3 /*break*/, 2]; - _b = (_a = Object).assign; - _c = [oper]; _d = {}; return [4 /*yield*/, generateNewId()]; - case 1: - _b.apply(_a, _c.concat([(_d.id = _e.sent(), _d)])); - this.operations.push(operation); - _e.label = 2; case 2: + _a[_b] = (_c.operation = (_d.id = _e.sent(), + _d.action = 'create', + _d.data = Object.assign(item, { id: id }), + _d), + _c); this.setDirty(); return [2 /*return*/]; } }); }); }; + ListNode.prototype.removeItem = function (id, beforeExecute, afterExecute) { + return tslib_1.__awaiter(this, void 0, void 0, function () { + var _a, _b; + var _c, _d; + return tslib_1.__generator(this, function (_e) { + switch (_e.label) { + case 0: + if (!(this.updates[id] && this.updates[id].operation.action === 'create')) return [3 /*break*/, 1]; + // 如果是新增项,在这里抵消 + (0, lodash_1.unset)(this.updates, id); + return [3 /*break*/, 3]; + case 1: + _a = this.updates; + _b = id; + _c = { + beforeExecute: beforeExecute, + afterExecute: afterExecute + }; + _d = {}; + return [4 /*yield*/, generateNewId()]; + case 2: + _a[_b] = (_c.operation = (_d.id = _e.sent(), + _d.action = 'remove', + _d.data = {}, + _d.filter = { + id: id, + }, + _d), + _c); + _e.label = 3; + case 3: + this.setDirty(); + return [2 /*return*/]; + } + }); + }); + }; + ListNode.prototype.recoverItem = function (id) { + return tslib_1.__awaiter(this, void 0, void 0, function () { + var operation; + return tslib_1.__generator(this, function (_a) { + operation = this.updates[id].operation; + (0, assert_1.assert)(operation.action === 'remove'); + (0, lodash_1.unset)(this.updates, id); + return [2 /*return*/]; + }); + }); + }; + /** + * 目前只支持根据itemId进行更新 + * @param data + * @param id + * @param beforeExecute + * @param afterExecute + */ + ListNode.prototype.updateItem = function (data, id, action, beforeExecute, afterExecute) { + return tslib_1.__awaiter(this, void 0, void 0, function () { + var operation, dataOrigin, _a, _b; + var _c, _d; + return tslib_1.__generator(this, function (_e) { + switch (_e.label) { + case 0: + (0, assert_1.assert)(Object.keys(this.children).length === 0, "\u66F4\u65B0\u5B50\u7ED3\u70B9\u5E94\u8BE5\u843D\u5728\u76F8\u5E94\u7684component\u4E0A"); + if (!this.updates[id]) return [3 /*break*/, 1]; + operation = this.updates[id].operation; + dataOrigin = operation.data; + (0, lodash_1.merge)(dataOrigin, data); + if (action && operation.action !== action) { + (0, assert_1.assert)(operation.action === 'update'); + operation.action = action; + } + return [3 /*break*/, 3]; + case 1: + _a = this.updates; + _b = id; + _c = { + beforeExecute: beforeExecute, + afterExecute: afterExecute + }; + _d = {}; + return [4 /*yield*/, generateNewId()]; + case 2: + _a[_b] = (_c.operation = (_d.id = _e.sent(), + _d.action = action || 'update', + _d.data = data, + _d.filter = { + id: id, + }, + _d), + _c); + _e.label = 3; + case 3: + this.setDirty(); + return [2 /*return*/]; + } + }); + }); + }; + ListNode.prototype.updateItems = function (data, action) { + return tslib_1.__awaiter(this, void 0, void 0, function () { + var _a, _b, _i, id; + return tslib_1.__generator(this, function (_c) { + switch (_c.label) { + case 0: + _a = []; + for (_b in data) + _a.push(_b); + _i = 0; + _c.label = 1; + case 1: + if (!(_i < _a.length)) return [3 /*break*/, 4]; + id = _a[_i]; + return [4 /*yield*/, this.updateItem(data[id], id, action)]; + case 2: + _c.sent(); + _c.label = 3; + case 3: + _i++; + return [3 /*break*/, 1]; + case 4: return [2 /*return*/]; + } + }); + }); + }; ListNode.prototype.doBeforeTrigger = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { - var _a, _b, operation, e_11_1, _c, _d, _i, k; - var e_11, _e; + var _a, _b, _i, k, update, _c, _d, _e, k; return tslib_1.__generator(this, function (_f) { switch (_f.label) { case 0: - _f.trys.push([0, 5, 6, 7]); - _a = tslib_1.__values(this.operations), _b = _a.next(); + _a = []; + for (_b in this.updates) + _a.push(_b); + _i = 0; _f.label = 1; case 1: - if (!!_b.done) return [3 /*break*/, 4]; - operation = _b.value; - if (!operation.beforeExecute) return [3 /*break*/, 3]; - return [4 /*yield*/, operation.beforeExecute()]; + if (!(_i < _a.length)) return [3 /*break*/, 4]; + k = _a[_i]; + update = this.updates[k]; + if (!update.beforeExecute) return [3 /*break*/, 3]; + return [4 /*yield*/, update.beforeExecute()]; case 2: _f.sent(); _f.label = 3; case 3: - _b = _a.next(); + _i++; return [3 /*break*/, 1]; - case 4: return [3 /*break*/, 7]; - case 5: - e_11_1 = _f.sent(); - e_11 = { error: e_11_1 }; - return [3 /*break*/, 7]; - case 6: - try { - if (_b && !_b.done && (_e = _a.return)) _e.call(_a); - } - finally { if (e_11) throw e_11.error; } - return [7 /*endfinally*/]; - case 7: + case 4: _c = []; for (_d in this.children) _c.push(_d); - _i = 0; - _f.label = 8; - case 8: - if (!(_i < _c.length)) return [3 /*break*/, 11]; - k = _c[_i]; + _e = 0; + _f.label = 5; + case 5: + if (!(_e < _c.length)) return [3 /*break*/, 8]; + k = _c[_e]; return [4 /*yield*/, this.children[k].doBeforeTrigger()]; - case 9: + case 6: _f.sent(); - _f.label = 10; - case 10: - _i++; - return [3 /*break*/, 8]; - case 11: return [2 /*return*/]; + _f.label = 7; + case 7: + _e++; + return [3 /*break*/, 5]; + case 8: return [2 /*return*/]; } }); }); }; ListNode.prototype.doAfterTrigger = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { - var _a, _b, operation, e_12_1, _c, _d, _i, k; - var e_12, _e; + var _a, _b, _i, k, update, _c, _d, _e, k; return tslib_1.__generator(this, function (_f) { switch (_f.label) { case 0: - _f.trys.push([0, 5, 6, 7]); - _a = tslib_1.__values(this.operations), _b = _a.next(); + _a = []; + for (_b in this.updates) + _a.push(_b); + _i = 0; _f.label = 1; case 1: - if (!!_b.done) return [3 /*break*/, 4]; - operation = _b.value; - if (!operation.afterExecute) return [3 /*break*/, 3]; - return [4 /*yield*/, operation.afterExecute()]; + if (!(_i < _a.length)) return [3 /*break*/, 4]; + k = _a[_i]; + update = this.updates[k]; + if (!update.afterExecute) return [3 /*break*/, 3]; + return [4 /*yield*/, update.afterExecute()]; case 2: _f.sent(); _f.label = 3; case 3: - _b = _a.next(); + _i++; return [3 /*break*/, 1]; - case 4: return [3 /*break*/, 7]; - case 5: - e_12_1 = _f.sent(); - e_12 = { error: e_12_1 }; - return [3 /*break*/, 7]; - case 6: - try { - if (_b && !_b.done && (_e = _a.return)) _e.call(_a); - } - finally { if (e_12) throw e_12.error; } - return [7 /*endfinally*/]; - case 7: + case 4: _c = []; for (_d in this.children) _c.push(_d); - _i = 0; - _f.label = 8; - case 8: - if (!(_i < _c.length)) return [3 /*break*/, 11]; - k = _c[_i]; + _e = 0; + _f.label = 5; + case 5: + if (!(_e < _c.length)) return [3 /*break*/, 8]; + k = _c[_e]; return [4 /*yield*/, this.children[k].doAfterTrigger()]; - case 9: + case 6: _f.sent(); - _f.label = 10; - case 10: - _i++; - return [3 /*break*/, 8]; - case 11: return [2 /*return*/]; + _f.label = 7; + case 7: + _e++; + return [3 /*break*/, 5]; + case 8: return [2 /*return*/]; } }); }); @@ -1044,15 +1136,6 @@ var ListNode = /** @class */ (function (_super) { return tslib_1.__awaiter(this, void 0, void 0, function () { var id; return tslib_1.__generator(this, function (_a) { - /* let idx = 0; - while (idx < this.ids!.length) { - if (this.children[idx] === childNode) { - return { - id: this.ids![idx], - } - } - idx++; - } */ for (id in this.children) { if (this.children[id] === childNode) { return [2 /*return*/, { @@ -1066,81 +1149,53 @@ var ListNode = /** @class */ (function (_super) { }; ListNode.prototype.composeOperations = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { - var childOperations, operations, childOperations_1, childOperations_1_1, oper, _a, index, eliminated, result, eliminated_3, eliminated_3_1, eli; - var e_13, _b, e_14, _c; + var childOperations, _a, _b, _i, id, childOperation, operations, id, operation, childOperation; var _this = this; - return tslib_1.__generator(this, function (_d) { - switch (_d.label) { + return tslib_1.__generator(this, function (_c) { + switch (_c.label) { case 0: if (!this.dirty) { return [2 /*return*/]; } - return [4 /*yield*/, Promise.all(Object.keys(this.children).map(function (ele) { return tslib_1.__awaiter(_this, void 0, void 0, function () { - var child, childOperations; - return tslib_1.__generator(this, function (_a) { - switch (_a.label) { - case 0: - child = this.children[ele]; - return [4 /*yield*/, child.composeOperations()]; - case 1: - childOperations = _a.sent(); - if (childOperations) { - (0, assert_1.assert)(childOperations.length === 1); - return [2 /*return*/, childOperations[0]]; - } - return [2 /*return*/]; - } - }); - }); }))]; + childOperations = {}; + _a = []; + for (_b in this.children) + _a.push(_b); + _i = 0; + _c.label = 1; case 1: - childOperations = _d.sent(); - operations = (0, lodash_1.cloneDeep)(this.operations.map(function (ele) { return ele.oper; })); - try { - for (childOperations_1 = tslib_1.__values(childOperations), childOperations_1_1 = childOperations_1.next(); !childOperations_1_1.done; childOperations_1_1 = childOperations_1.next()) { - oper = childOperations_1_1.value; - if (oper) { - _a = findOperationToMerge(this.entity, this.schema, oper, operations), index = _a.index, eliminated = _a.eliminated; - if (index) { - result = mergeOperationOper(this.entity, this.schema, oper, index); - if (result) { - // 说明相互抵消了 - (0, lodash_1.pull)(operations, index); - } - else { - } - } - else { - operations.push(oper); - } - try { - for (eliminated_3 = (e_14 = void 0, tslib_1.__values(eliminated)), eliminated_3_1 = eliminated_3.next(); !eliminated_3_1.done; eliminated_3_1 = eliminated_3.next()) { - eli = eliminated_3_1.value; - if (eli) { - (0, lodash_1.pull)(operations, eli); - } - } - } - catch (e_14_1) { e_14 = { error: e_14_1 }; } - finally { - try { - if (eliminated_3_1 && !eliminated_3_1.done && (_c = eliminated_3.return)) _c.call(eliminated_3); - } - finally { if (e_14) throw e_14.error; } - } - } - } - } - catch (e_13_1) { e_13 = { error: e_13_1 }; } - finally { - try { - if (childOperations_1_1 && !childOperations_1_1.done && (_b = childOperations_1.return)) _b.call(childOperations_1); - } - finally { if (e_13) throw e_13.error; } - } - return [4 /*yield*/, repairOperations(this.entity, this.schema, operations)]; + if (!(_i < _a.length)) return [3 /*break*/, 4]; + id = _a[_i]; + return [4 /*yield*/, this.children[id].composeOperations()]; case 2: - _d.sent(); - return [2 /*return*/, operations.map(function (ele) { return Object.assign(ele, { + childOperation = _c.sent(); + if (childOperation) { + (0, assert_1.assert)(childOperation.length === 1); + childOperations[id] = childOperation[0].operation; + } + _c.label = 3; + case 3: + _i++; + return [3 /*break*/, 1]; + case 4: + operations = []; + for (id in this.updates) { + operation = (0, lodash_1.cloneDeep)(this.updates[id].operation); + if (childOperations[id]) { + childOperation = childOperations[id]; + // 在list有operation在singleNode上也有目前只允许一种情况,即list上create,在single上update + (0, assert_1.assert)(operation.action === 'create' && childOperation.action === 'update'); + Object.assign(operation.data, childOperation.data); + (0, lodash_1.unset)(childOperations, id); + } + operations.push(operation); + } + operations.push.apply(operations, tslib_1.__spreadArray([], tslib_1.__read(Object.values(childOperations)), false)); + return [4 /*yield*/, repairOperations(this.entity, this.schema, operations)]; + case 5: + _c.sent(); + return [2 /*return*/, operations.map(function (ele) { return Object.assign({ + operation: ele, entity: _this.entity, }); })]; } @@ -1326,7 +1381,7 @@ var ListNode = /** @class */ (function (_super) { }; ListNode.prototype.clean = function () { this.dirty = undefined; - this.operations = []; + this.updates = {}; for (var k in this.children) { this.children[k].clean(); } @@ -1341,8 +1396,34 @@ var SingleNode = /** @class */ (function (_super) { if (id) { _this.id = id; } + else { + // 若没有父结点上的filter,则一定是create动作 + _this.tryGetParentFilter() + .then(function () { return _this.create({}); }); + } return _this; } + SingleNode.prototype.tryGetParentFilter = function (disableOperation) { + return tslib_1.__awaiter(this, void 0, void 0, function () { + var parent, filter; + return tslib_1.__generator(this, function (_a) { + switch (_a.label) { + case 0: + parent = this.getParent(); + if (!(parent instanceof SingleNode)) return [3 /*break*/, 2]; + return [4 /*yield*/, parent.getParentFilter(this, disableOperation)]; + case 1: + filter = _a.sent(); + return [2 /*return*/, filter]; + case 2: + if (!(parent instanceof ListNode)) return [3 /*break*/, 4]; + return [4 /*yield*/, parent.getParentFilter(this)]; + case 3: return [2 /*return*/, _a.sent()]; + case 4: return [2 /*return*/]; + } + }); + }); + }; SingleNode.prototype.getChildPath = function (child) { for (var k in this.children) { if (child === this.children[k]) { @@ -1359,7 +1440,7 @@ var SingleNode = /** @class */ (function (_super) { }; SingleNode.prototype.checkIfClean = function () { var _a; - if (this.operations.length > 0) { + if (this.operation) { return; } for (var k in this.children) { @@ -1412,8 +1493,7 @@ var SingleNode = /** @class */ (function (_super) { }; SingleNode.prototype.getFreshValue = function (disableOperation) { return tslib_1.__awaiter(this, void 0, void 0, function () { - var projection, modies, _a, operations, filter, createOper, parent_1, result; - var _this = this; + var projection, modies, _a, operations, filter, result; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, this.getProjection(false)]; @@ -1429,29 +1509,19 @@ var SingleNode = /** @class */ (function (_super) { modies = _a; operations = modies ? (0, modi_1.createOperationsFromModies)(modies) : []; filter = this.id && { id: this.id }; - if (!filter && !disableOperation) { - createOper = this.operations.find(function (ele) { return ele.oper.action === 'create'; }); - if (createOper) { - (0, assert_1.assert)(createOper.oper.data.id); - filter = { - id: createOper.oper.data.id - }; - } - } if (!!filter) return [3 /*break*/, 5]; - parent_1 = this.getParent(); - if (!(parent_1 instanceof ListNode || parent_1 instanceof SingleNode)) return [3 /*break*/, 5]; - return [4 /*yield*/, parent_1.getParentFilter(this, disableOperation)]; + return [4 /*yield*/, this.tryGetParentFilter(disableOperation)]; case 4: + // 还可能是来自父级的外键 filter = _b.sent(); _b.label = 5; case 5: if (!filter) return [3 /*break*/, 7]; - if (!disableOperation) { - operations.push.apply(operations, tslib_1.__spreadArray([], tslib_1.__read(this.operations.map(function (ele) { return ({ - entity: _this.entity, - operation: ele.oper, - }); })), false)); + if (!disableOperation && this.operation) { + operations.push({ + entity: this.entity, + operation: this.operation.operation, + }); } return [4 /*yield*/, this.cache.tryRedoOperationsThenSelect(this.entity, { data: projection, @@ -1466,163 +1536,170 @@ var SingleNode = /** @class */ (function (_super) { }); }; SingleNode.prototype.doBeforeTrigger = function () { + var _a; return tslib_1.__awaiter(this, void 0, void 0, function () { - var _a, _b, operation, e_15_1, _c, _d, _i, k, child; - var e_15, _e; - return tslib_1.__generator(this, function (_f) { - switch (_f.label) { + var _b, _c, _i, k, child; + return tslib_1.__generator(this, function (_d) { + switch (_d.label) { case 0: - _f.trys.push([0, 5, 6, 7]); - _a = tslib_1.__values(this.operations), _b = _a.next(); - _f.label = 1; + if (!((_a = this.operation) === null || _a === void 0 ? void 0 : _a.beforeExecute)) return [3 /*break*/, 2]; + return [4 /*yield*/, this.operation.beforeExecute()]; case 1: - if (!!_b.done) return [3 /*break*/, 4]; - operation = _b.value; - if (!operation.beforeExecute) return [3 /*break*/, 3]; - return [4 /*yield*/, operation.beforeExecute()]; + _d.sent(); + _d.label = 2; case 2: - _f.sent(); - _f.label = 3; - case 3: - _b = _a.next(); - return [3 /*break*/, 1]; - case 4: return [3 /*break*/, 7]; - case 5: - e_15_1 = _f.sent(); - e_15 = { error: e_15_1 }; - return [3 /*break*/, 7]; - case 6: - try { - if (_b && !_b.done && (_e = _a.return)) _e.call(_a); - } - finally { if (e_15) throw e_15.error; } - return [7 /*endfinally*/]; - case 7: - _c = []; - for (_d in this.children) - _c.push(_d); + _b = []; + for (_c in this.children) + _b.push(_c); _i = 0; - _f.label = 8; - case 8: - if (!(_i < _c.length)) return [3 /*break*/, 11]; - k = _c[_i]; + _d.label = 3; + case 3: + if (!(_i < _b.length)) return [3 /*break*/, 6]; + k = _b[_i]; child = this.children[k]; return [4 /*yield*/, child.doBeforeTrigger()]; - case 9: - _f.sent(); - _f.label = 10; - case 10: + case 4: + _d.sent(); + _d.label = 5; + case 5: _i++; - return [3 /*break*/, 8]; - case 11: return [2 /*return*/]; + return [3 /*break*/, 3]; + case 6: return [2 /*return*/]; } }); }); }; SingleNode.prototype.doAfterTrigger = function () { + var _a; return tslib_1.__awaiter(this, void 0, void 0, function () { - var _a, _b, operation, e_16_1, _c, _d, _i, k, child; - var e_16, _e; - return tslib_1.__generator(this, function (_f) { - switch (_f.label) { + var _b, _c, _i, k, child; + return tslib_1.__generator(this, function (_d) { + switch (_d.label) { case 0: - _f.trys.push([0, 5, 6, 7]); - _a = tslib_1.__values(this.operations), _b = _a.next(); - _f.label = 1; + if (!((_a = this.operation) === null || _a === void 0 ? void 0 : _a.afterExecute)) return [3 /*break*/, 2]; + return [4 /*yield*/, this.operation.afterExecute()]; case 1: - if (!!_b.done) return [3 /*break*/, 4]; - operation = _b.value; - if (!operation.afterExecute) return [3 /*break*/, 3]; - return [4 /*yield*/, operation.afterExecute()]; + _d.sent(); + _d.label = 2; case 2: - _f.sent(); - _f.label = 3; - case 3: - _b = _a.next(); - return [3 /*break*/, 1]; - case 4: return [3 /*break*/, 7]; - case 5: - e_16_1 = _f.sent(); - e_16 = { error: e_16_1 }; - return [3 /*break*/, 7]; - case 6: - try { - if (_b && !_b.done && (_e = _a.return)) _e.call(_a); - } - finally { if (e_16) throw e_16.error; } - return [7 /*endfinally*/]; - case 7: - _c = []; - for (_d in this.children) - _c.push(_d); + _b = []; + for (_c in this.children) + _b.push(_c); _i = 0; - _f.label = 8; - case 8: - if (!(_i < _c.length)) return [3 /*break*/, 11]; - k = _c[_i]; + _d.label = 3; + case 3: + if (!(_i < _b.length)) return [3 /*break*/, 6]; + k = _b[_i]; child = this.children[k]; return [4 /*yield*/, child.doAfterTrigger()]; - case 9: - _f.sent(); - _f.label = 10; - case 10: + case 4: + _d.sent(); + _d.label = 5; + case 5: _i++; - return [3 /*break*/, 8]; - case 11: return [2 /*return*/]; + return [3 /*break*/, 3]; + case 6: return [2 /*return*/]; } }); }); }; - SingleNode.prototype.addOperation = function (oper, beforeExecute, afterExecute) { + SingleNode.prototype.create = function (data, beforeExecute, afterExecute) { return tslib_1.__awaiter(this, void 0, void 0, function () { - var operation, id, _a, _b, _c, result; - var _d; - return tslib_1.__generator(this, function (_e) { - switch (_e.label) { + var id, _a; + var _b, _c; + return tslib_1.__generator(this, function (_d) { + switch (_d.label) { + case 0: return [4 /*yield*/, generateNewId()]; + case 1: + id = _d.sent(); + this.id = id; + _a = this; + _b = {}; + _c = {}; + return [4 /*yield*/, generateNewId()]; + case 2: + _a.operation = (_b.operation = (_c.id = _d.sent(), + _c.action = 'create', + _c.data = Object.assign({}, data, { id: id }), + _c), + _b.beforeExecute = beforeExecute, + _b.afterExecute = afterExecute, + _b); + this.setDirty(); + return [2 /*return*/]; + } + }); + }); + }; + SingleNode.prototype.update = function (data, action, beforeExecute, afterExecute) { + return tslib_1.__awaiter(this, void 0, void 0, function () { + var operation, operation; + var _a; + return tslib_1.__generator(this, function (_b) { + switch (_b.label) { case 0: + if (!!this.operation) return [3 /*break*/, 2]; + _a = {}; + return [4 /*yield*/, generateNewId()]; + case 1: + operation = (_a.id = _b.sent(), + _a.action = action || 'update', + _a.data = data, + _a); if (this.id) { - if (oper.action === 'create') { - oper.action = 'update'; - } - if (!oper.filter) { - Object.assign(oper, { - filter: { - id: this.id, - }, - }); - } - else { - (0, assert_1.assert)(oper.filter.id === this.id); - } + Object.assign(operation, { + filter: { + id: this.id, + }, + }); } - operation = { - oper: oper, + this.operation = { + operation: operation, beforeExecute: beforeExecute, afterExecute: afterExecute, }; - if (!(this.operations.length === 0)) return [3 /*break*/, 4]; - if (!(oper.action === 'create')) return [3 /*break*/, 2]; + return [3 /*break*/, 3]; + case 2: + operation = this.operation.operation; + (0, assert_1.assert)(['create', 'update', action].includes(operation.action)); + Object.assign(operation.data, data); + if (action && operation.action !== action) { + operation.action = action; + } + _b.label = 3; + case 3: + this.setDirty(); + return [2 /*return*/]; + } + }); + }); + }; + SingleNode.prototype.remove = function (beforeExecute, afterExecute) { + return tslib_1.__awaiter(this, void 0, void 0, function () { + var operation; + var _a; + return tslib_1.__generator(this, function (_b) { + switch (_b.label) { + case 0: + _a = {}; return [4 /*yield*/, generateNewId()]; case 1: - id = _e.sent(); - Object.assign(oper.data, { - id: id, - }); - _e.label = 2; - case 2: - _b = (_a = Object).assign; - _c = [oper]; - _d = {}; - return [4 /*yield*/, generateNewId()]; - case 3: - _b.apply(_a, _c.concat([(_d.id = _e.sent(), _d)])); - this.operations.push(operation); - return [3 /*break*/, 5]; - case 4: - result = mergeOperationOper(this.entity, this.schema, oper, this.operations[0].oper); - (0, assert_1.assert)(!result); - _e.label = 5; - case 5: + operation = (_a.id = _b.sent(), + _a.action = 'remove', + _a.data = {}, + _a); + if (this.id) { + Object.assign(operation, { + filter: { + id: this.id, + }, + }); + } + this.operation = { + operation: operation, + beforeExecute: beforeExecute, + afterExecute: afterExecute, + }; this.setDirty(); return [2 /*return*/]; } @@ -1631,121 +1708,76 @@ var SingleNode = /** @class */ (function (_super) { }; SingleNode.prototype.composeOperations = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { - var childOperations, operations, _a, _b, _c, _d, childOperations_2, childOperations_2_1, oper; - var _e, _f, _g, e_17, _h; - var _this = this; - return tslib_1.__generator(this, function (_j) { - switch (_j.label) { + var operation, _a, _b, _c, _i, ele, child, childOperations, sliceIdx, ele2, childOpers; + var _d, _e, _f; + return tslib_1.__generator(this, function (_g) { + switch (_g.label) { case 0: if (!this.dirty) { return [2 /*return*/]; } - return [4 /*yield*/, Promise.all(Object.keys(this.children).map(function (ele) { return tslib_1.__awaiter(_this, void 0, void 0, function () { - var child, childOperations, subOper, sliceIdx, ele2; - var _a, _b; - return tslib_1.__generator(this, function (_c) { - switch (_c.label) { - case 0: - child = this.children[ele]; - return [4 /*yield*/, child.composeOperations()]; - case 1: - childOperations = _c.sent(); - if (childOperations) { - if (child instanceof SingleNode) { - subOper = childOperations[0]; - } - else { - (0, assert_1.assert)(child instanceof ListNode); - subOper = childOperations; - } - } - if (subOper) { - sliceIdx = ele.indexOf(':'); - ele2 = sliceIdx > 0 ? ele.slice(0, sliceIdx) : ele; - if (this.id) { - return [2 /*return*/, { - id: 'dummy', - action: 'update', - data: (_a = {}, - _a[ele2] = subOper, - _a), - filter: { - id: this.id, - } - }]; - } - else { - return [2 /*return*/, { - id: 'dummy', - action: 'create', - data: (_b = {}, - _b[ele2] = subOper, - _b), - }]; - } - } - return [2 /*return*/]; - } - }); - }); }))]; + if (!this.operation) return [3 /*break*/, 1]; + _a = (0, lodash_1.cloneDeep)(this.operation.operation); + return [3 /*break*/, 3]; case 1: - childOperations = _j.sent(); - operations = []; - if (!(this.operations.length > 0)) return [3 /*break*/, 2]; - (0, assert_1.assert)(this.operations.length === 1); - // 这里不能直接改this.operations,只能克隆一个新的 - operations.push((0, lodash_1.cloneDeep)(this.operations[0].oper)); - return [3 /*break*/, 7]; + _d = {}; + return [4 /*yield*/, generateNewId()]; case 2: - if (!this.id) return [3 /*break*/, 4]; - _b = (_a = operations).push; - _e = {}; - return [4 /*yield*/, generateNewId()]; + _a = (_d.id = _g.sent(), + _d.action = 'update', + _d.data = {}, + _d); + _g.label = 3; case 3: - _b.apply(_a, [(_e.id = _j.sent(), - _e.action = 'update', - _e.data = {}, - _e.filter = { + operation = _a; + if (this.id && !operation.filter) { + Object.assign(operation, { + filter: { id: this.id, - }, - _e)]); - return [3 /*break*/, 7]; + } + }); + } + _b = []; + for (_c in this.children) + _b.push(_c); + _i = 0; + _g.label = 4; case 4: - _d = (_c = operations).push; - _f = {}; - return [4 /*yield*/, generateNewId()]; + if (!(_i < _b.length)) return [3 /*break*/, 7]; + ele = _b[_i]; + child = this.children[ele]; + return [4 /*yield*/, child.composeOperations()]; case 5: - _f.id = _j.sent(), - _f.action = 'create'; - _g = {}; - return [4 /*yield*/, generateNewId()]; - case 6: - _d.apply(_c, [(_f.data = (_g.id = _j.sent(), - _g), - _f)]); - _j.label = 7; - case 7: - try { - for (childOperations_2 = tslib_1.__values(childOperations), childOperations_2_1 = childOperations_2.next(); !childOperations_2_1.done; childOperations_2_1 = childOperations_2.next()) { - oper = childOperations_2_1.value; - if (oper) { - mergeOperationOper(this.entity, this.schema, oper, operations[0]); // SingleNode貌似不可能不merge成功 + childOperations = _g.sent(); + sliceIdx = ele.indexOf(':'); + ele2 = sliceIdx > 0 ? ele.slice(0, sliceIdx) : ele; + if (childOperations) { + if (child instanceof SingleNode) { + (0, assert_1.assert)(childOperations.length === 1); + (0, assert_1.assert)(!operation.data[ele2]); // 多对一的子结点不应该有多项 + Object.assign(operation.data, (_e = {}, + _e[ele2] = childOperations[0].operation, + _e)); + } + else { + (0, assert_1.assert)(child instanceof ListNode); + childOpers = childOperations.map(function (ele) { return ele.operation; }); + if (operation.data[ele2]) { + (_f = operation.data[ele2]).push.apply(_f, tslib_1.__spreadArray([], tslib_1.__read(childOpers), false)); + } + else { + operation.data[ele2] = childOpers; } } } - catch (e_17_1) { e_17 = { error: e_17_1 }; } - finally { - try { - if (childOperations_2_1 && !childOperations_2_1.done && (_h = childOperations_2.return)) _h.call(childOperations_2); - } - finally { if (e_17) throw e_17.error; } - } - return [4 /*yield*/, repairOperations(this.entity, this.schema, operations)]; - case 8: - _j.sent(); - return [2 /*return*/, operations.map(function (ele) { return Object.assign(ele, { - entity: _this.entity, - }); })]; + _g.label = 6; + case 6: + _i++; + return [3 /*break*/, 4]; + case 7: return [2 /*return*/, [{ + entity: this.entity, + operation: operation, + }]]; } }); }); @@ -1818,12 +1850,14 @@ var SingleNode = /** @class */ (function (_super) { }; SingleNode.prototype.refresh = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { - var parentFilter, id, projection, filter, _a, value, modi$entity, err_2; + var id, parentFilter, id2, projection, filter, _a, value, modi$entity, err_2; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: - if (!!this.id) return [3 /*break*/, 3]; + id = this.id; + if (!!id) return [3 /*break*/, 3]; if (!(this.parent instanceof ListNode)) return [3 /*break*/, 1]; + // list的子项不需要refresh,由list负责刷新数据 (0, assert_1.assert)(this.parent.getEntity() === this.entity); // id = this.parent.getChildPath(this); // this.id = id; @@ -1834,18 +1868,18 @@ var SingleNode = /** @class */ (function (_super) { case 2: parentFilter = _b.sent(); if (parentFilter) { - id = parentFilter.id; - this.id = id; + id2 = parentFilter.id; + id = id2; } _b.label = 3; case 3: - if (!this.id) { + if (!id) { return [2 /*return*/]; } return [4 /*yield*/, this.getProjection()]; case 4: projection = _b.sent(); - filter = { id: this.id }; + filter = { id: id }; this.setLoading(true); _b.label = 5; case 5: @@ -1874,7 +1908,7 @@ var SingleNode = /** @class */ (function (_super) { }; SingleNode.prototype.clean = function () { this.dirty = undefined; - this.operations = []; + this.operation = undefined; for (var child in this.children) { this.children[child].clean(); } @@ -2171,8 +2205,8 @@ function repairOperations(entity, schema, operations) { }); }); } - var operations_1, operations_1_1, operation, _a, data, data_1, data_1_1, d, e_18_1, e_19_1; - var e_19, _b, e_18, _c; + var operations_1, operations_1_1, operation, _a, data, data_1, data_1_1, d, e_10_1, e_11_1; + var e_11, _b, e_10, _c; return tslib_1.__generator(this, function (_d) { switch (_d.label) { case 0: @@ -2194,7 +2228,7 @@ function repairOperations(entity, schema, operations) { _d.label = 4; case 4: _d.trys.push([4, 9, 10, 11]); - data_1 = (e_18 = void 0, tslib_1.__values(data)), data_1_1 = data_1.next(); + data_1 = (e_10 = void 0, tslib_1.__values(data)), data_1_1 = data_1.next(); _d.label = 5; case 5: if (!!data_1_1.done) return [3 /*break*/, 8]; @@ -2208,14 +2242,14 @@ function repairOperations(entity, schema, operations) { return [3 /*break*/, 5]; case 8: return [3 /*break*/, 11]; case 9: - e_18_1 = _d.sent(); - e_18 = { error: e_18_1 }; + e_10_1 = _d.sent(); + e_10 = { error: e_10_1 }; return [3 /*break*/, 11]; case 10: try { if (data_1_1 && !data_1_1.done && (_c = data_1.return)) _c.call(data_1); } - finally { if (e_18) throw e_18.error; } + finally { if (e_10) throw e_10.error; } return [7 /*endfinally*/]; case 11: return [3 /*break*/, 14]; case 12: return [4 /*yield*/, repairData(entity, data)]; @@ -2227,14 +2261,14 @@ function repairOperations(entity, schema, operations) { return [3 /*break*/, 1]; case 15: return [3 /*break*/, 18]; case 16: - e_19_1 = _d.sent(); - e_19 = { error: e_19_1 }; + e_11_1 = _d.sent(); + e_11 = { error: e_11_1 }; return [3 /*break*/, 18]; case 17: try { if (operations_1_1 && !operations_1_1.done && (_b = operations_1.return)) _b.call(operations_1); } - finally { if (e_19) throw e_19.error; } + finally { if (e_11) throw e_11.error; } return [7 /*endfinally*/]; case 18: return [2 /*return*/]; } @@ -2269,10 +2303,12 @@ var RunningTree = /** @class */ (function (_super) { } if (entity) { if (isList) { + (0, assert_1.assert)(!(parentNode instanceof ListNode)); node = new ListNode(entity, this.schema, this.cache, projection, parentNode, filters, sorters, pagination); } else { - node = new SingleNode(entity, this.schema, this.cache, projection, parentNode, id); + node = new SingleNode(entity, this.schema, this.cache, projection, parentNode, // 过编译 + id); } } else { @@ -2311,14 +2347,14 @@ var RunningTree = /** @class */ (function (_super) { var node = this.findNode(path); if (node) { var childPath = path.slice(path.lastIndexOf('.') + 1); - var parent_2 = node.getParent(); - if (parent_2 instanceof SingleNode) { - parent_2.removeChild(childPath); + var parent_1 = node.getParent(); + if (parent_1 instanceof SingleNode) { + parent_1.removeChild(childPath); } - else if (parent_2 instanceof ListNode) { - parent_2.removeChild(childPath); + else if (parent_1 instanceof ListNode) { + parent_1.removeChild(childPath); } - else if (!parent_2) { + else if (!parent_1) { (0, assert_1.assert)(this.root.hasOwnProperty(path)); (0, lodash_1.unset)(this.root, path); } @@ -2334,15 +2370,111 @@ var RunningTree = /** @class */ (function (_super) { var node = this.findNode(path); return node ? node.isDirty() : false; }; - RunningTree.prototype.addOperation = function (path, operation, beforeExecute, afterExecute) { + RunningTree.prototype.addItem = function (path, data, beforeExecute, afterExecute) { return tslib_1.__awaiter(this, void 0, void 0, function () { var node; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: node = this.findNode(path); - (0, assert_1.assert)(node && (node instanceof SingleNode || node instanceof ListNode)); - return [4 /*yield*/, node.addOperation(operation, beforeExecute, afterExecute)]; + (0, assert_1.assert)(node instanceof ListNode); + return [4 /*yield*/, node.addItem(data, beforeExecute, afterExecute)]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + RunningTree.prototype.removeItem = function (path, id, beforeExecute, afterExecute) { + return tslib_1.__awaiter(this, void 0, void 0, function () { + var node; + return tslib_1.__generator(this, function (_a) { + switch (_a.label) { + case 0: + node = this.findNode(path); + (0, assert_1.assert)(node instanceof ListNode); + return [4 /*yield*/, node.removeItem(id, beforeExecute, afterExecute)]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + RunningTree.prototype.updateItem = function (path, data, id, action, beforeExecute, afterExecute) { + return tslib_1.__awaiter(this, void 0, void 0, function () { + var node; + return tslib_1.__generator(this, function (_a) { + switch (_a.label) { + case 0: + node = this.findNode(path); + (0, assert_1.assert)(node instanceof ListNode); + return [4 /*yield*/, node.updateItem(data, id, action, beforeExecute, afterExecute)]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + RunningTree.prototype.recoverItem = function (path, id) { + return tslib_1.__awaiter(this, void 0, void 0, function () { + var node; + return tslib_1.__generator(this, function (_a) { + switch (_a.label) { + case 0: + node = this.findNode(path); + (0, assert_1.assert)(node instanceof ListNode); + return [4 /*yield*/, node.recoverItem(id)]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + RunningTree.prototype.create = function (path, data, beforeExecute, afterExecute) { + return tslib_1.__awaiter(this, void 0, void 0, function () { + var node; + return tslib_1.__generator(this, function (_a) { + switch (_a.label) { + case 0: + node = this.findNode(path); + (0, assert_1.assert)(node instanceof SingleNode); + return [4 /*yield*/, node.create(data, beforeExecute, afterExecute)]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + RunningTree.prototype.update = function (path, data, action, beforeExecute, afterExecute) { + return tslib_1.__awaiter(this, void 0, void 0, function () { + var node; + return tslib_1.__generator(this, function (_a) { + switch (_a.label) { + case 0: + node = this.findNode(path); + (0, assert_1.assert)(node instanceof SingleNode); + return [4 /*yield*/, node.update(data, action, beforeExecute, afterExecute)]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + RunningTree.prototype.remove = function (path, beforeExecute, afterExecute) { + return tslib_1.__awaiter(this, void 0, void 0, function () { + var node; + return tslib_1.__generator(this, function (_a) { + switch (_a.label) { + case 0: + node = this.findNode(path); + (0, assert_1.assert)(node instanceof SingleNode); + return [4 /*yield*/, node.remove(beforeExecute, afterExecute)]; case 1: _a.sent(); return [2 /*return*/]; @@ -2599,51 +2731,55 @@ var RunningTree = /** @class */ (function (_super) { }); }); }; - RunningTree.prototype.execute = function (path, operation) { + RunningTree.prototype.execute = function (path, data, action) { return tslib_1.__awaiter(this, void 0, void 0, function () { var node, operations, entities, err_3; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: node = this.findNode(path); - if (!operation) return [3 /*break*/, 2]; - (0, assert_1.assert)(node instanceof ListNode || node instanceof SingleNode); - return [4 /*yield*/, node.addOperation(operation)]; + if (!data) return [3 /*break*/, 3]; + if (!(node instanceof SingleNode)) return [3 /*break*/, 2]; + return [4 /*yield*/, node.update(data, action)]; case 1: _a.sent(); - _a.label = 2; + return [3 /*break*/, 3]; case 2: - (0, assert_1.assert)(node.isDirty()); - node.setExecuting(true); + (0, assert_1.assert)(node instanceof ListNode); + node.updateItems(data); _a.label = 3; case 3: - _a.trys.push([3, 8, , 9]); - return [4 /*yield*/, node.doBeforeTrigger()]; + (0, assert_1.assert)(node.isDirty()); + node.setExecuting(true); + _a.label = 4; case 4: + _a.trys.push([4, 9, , 10]); + return [4 /*yield*/, node.doBeforeTrigger()]; + case 5: _a.sent(); return [4 /*yield*/, node.composeOperations()]; - case 5: + case 6: operations = (_a.sent()); entities = (0, lodash_1.uniq)(operations.filter(function (ele) { return !!ele; }).map(function (ele) { return ele.entity; })); (0, assert_1.assert)(entities.length === 1); return [4 /*yield*/, this.aspectWrapper.exec('operate', { entity: entities[0], - operation: operations.filter(function (ele) { return !!ele; }), + operation: operations.filter(function (ele) { return !!ele; }).map(function (ele) { return ele.operation; }), })]; - case 6: + case 7: _a.sent(); return [4 /*yield*/, node.doAfterTrigger()]; - case 7: + case 8: _a.sent(); // 清空缓存 node.clean(); node.setExecuting(false); return [2 /*return*/, operations]; - case 8: + case 9: err_3 = _a.sent(); node.setExecuting(false); throw err_3; - case 9: return [2 /*return*/]; + case 10: return [2 /*return*/]; } }); }); @@ -2667,7 +2803,22 @@ var RunningTree = /** @class */ (function (_super) { }; tslib_1.__decorate([ Feature_1.Action - ], RunningTree.prototype, "addOperation", null); + ], RunningTree.prototype, "removeItem", null); + tslib_1.__decorate([ + Feature_1.Action + ], RunningTree.prototype, "updateItem", null); + tslib_1.__decorate([ + Feature_1.Action + ], RunningTree.prototype, "recoverItem", null); + tslib_1.__decorate([ + Feature_1.Action + ], RunningTree.prototype, "create", null); + tslib_1.__decorate([ + Feature_1.Action + ], RunningTree.prototype, "update", null); + tslib_1.__decorate([ + Feature_1.Action + ], RunningTree.prototype, "remove", null); tslib_1.__decorate([ Feature_1.Action ], RunningTree.prototype, "refresh", null); diff --git a/lib/index.d.ts b/lib/index.d.ts index b45baad8..392af826 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -7,3 +7,4 @@ export * from './utils/upload'; export * from './types/Notification'; export * from './types/Message'; export * from './utils/bluetooth'; +export * from './types/Page'; diff --git a/lib/index.js b/lib/index.js index f1f4cd54..2e150ee5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,3 +9,4 @@ tslib_1.__exportStar(require("./utils/upload"), exports); tslib_1.__exportStar(require("./types/Notification"), exports); tslib_1.__exportStar(require("./types/Message"), exports); tslib_1.__exportStar(require("./utils/bluetooth"), exports); +tslib_1.__exportStar(require("./types/Page"), exports); diff --git a/lib/page.common.d.ts b/lib/page.common.d.ts index ee4644fb..ee14d9f3 100644 --- a/lib/page.common.d.ts +++ b/lib/page.common.d.ts @@ -1,16 +1,12 @@ import { Context, EntityDict } from 'oak-domain/lib/types'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; import { OakComponentOption, ComponentFullThisType } from './types/Page'; -export declare function subscribe>(this: ComponentFullThisType): void; -export declare function unsubscribe>(this: ComponentFullThisType): void; -export declare function onPathSet>(this: ComponentFullThisType, option: OakComponentOption): Promise; -export declare function reRender>(this: ComponentFullThisType, option: OakComponentOption, extra?: Record): Promise; -export declare function refresh>(this: ComponentFullThisType): Promise; -export declare function loadMore>(this: ComponentFullThisType): Promise; -export declare function execute>(this: ComponentFullThisType, operation?: Omit, path?: string): Promise; -export declare function callPicker>(this: ComponentFullThisType, attr: string, params?: Record): void; -export declare function setUpdateData>(this: ComponentFullThisType, attr: string, data: any): Promise; -export declare function setMultiAttrUpdateData>(this: ComponentFullThisType, data: Record): Promise; -export declare function destroyNode>(this: ComponentFullThisType): void; +export declare function subscribe>(this: ComponentFullThisType): void; +export declare function unsubscribe>(this: ComponentFullThisType): void; +export declare function onPathSet>(this: ComponentFullThisType, option: OakComponentOption): Promise; +export declare function reRender>(this: ComponentFullThisType, option: OakComponentOption, extra?: Record): Promise; +export declare function refresh>(this: ComponentFullThisType): Promise; +export declare function loadMore>(this: ComponentFullThisType): Promise; +export declare function execute>(this: ComponentFullThisType, data?: ED[T]['Update']['data'] | Record, path?: string): Promise; +export declare function callPicker>(this: ComponentFullThisType, attr: string, params?: Record): void; +export declare function destroyNode>(this: ComponentFullThisType): void; diff --git a/lib/page.common.js b/lib/page.common.js index 9a59f2b9..091a63e0 100644 --- a/lib/page.common.js +++ b/lib/page.common.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.destroyNode = exports.setMultiAttrUpdateData = exports.setUpdateData = exports.callPicker = exports.execute = exports.loadMore = exports.refresh = exports.reRender = exports.onPathSet = exports.unsubscribe = exports.subscribe = void 0; +exports.destroyNode = exports.callPicker = exports.execute = exports.loadMore = exports.refresh = exports.reRender = exports.onPathSet = exports.unsubscribe = exports.subscribe = void 0; var tslib_1 = require("tslib"); var assert_1 = require("oak-domain/lib/utils/assert"); var types_1 = require("oak-domain/lib/types"); @@ -138,13 +138,11 @@ function onPathSet(option) { Object.assign(this.state, { oakEntity: entity2, oakFullpath: oakPath2, - oakIsReady: true, }); return [3 /*break*/, 4]; case 2: Object.assign(this.state, { oakFullpath: oakPath2, - oakIsReady: true, }); // 创建virtualNode return [4 /*yield*/, features.runningTree.createNode({ @@ -265,7 +263,6 @@ function reRender(option, extra) { this.setState(data); return [3 /*break*/, 15]; case 11: - if (!this.state.oakFullpath) return [3 /*break*/, 15]; if (!formData) return [3 /*break*/, 13]; return [4 /*yield*/, formData.call(this, { features: features, @@ -346,53 +343,26 @@ function loadMore() { }); } exports.loadMore = loadMore; -function execute(operation, path) { +function execute(data, path) { return tslib_1.__awaiter(this, void 0, void 0, function () { - var fullpath, result, err_4, attrs, entity_1, message, attrNames; - var _this = this; + var fullpath, result; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: if (this.state.oakExecuting) { throw new Error('请仔细设计按钮状态,不要允许重复点击!'); } - _a.label = 1; + fullpath = path ? "".concat(this.state.oakFullpath, ".").concat(path) : this.state.oakFullpath; + return [4 /*yield*/, this.features.runningTree.execute(fullpath, data)]; case 1: - _a.trys.push([1, 4, , 5]); - fullpath = path - ? "".concat(this.state.oakFullpath, ".").concat(path) - : this.state.oakFullpath; - return [4 /*yield*/, this.features.runningTree.execute(fullpath, operation)]; - case 2: result = _a.sent(); return [4 /*yield*/, this.setMessage({ type: 'success', content: '操作成功', })]; - case 3: + case 2: _a.sent(); return [2 /*return*/, result]; - case 4: - err_4 = _a.sent(); - if (err_4 instanceof types_1.OakUserException) { - if (err_4 instanceof types_1.OakInputIllegalException) { - attrs = err_4.getAttributes(); - entity_1 = err_4.getEntity(); - message = err_4.message; - attrNames = attrs.map(function (attr) { return _this.t("".concat(entity_1, ":attr.").concat(attr)); }).filter(function (ele) { return !!ele; }); - this.setMessage({ - type: 'error', - content: attrNames.length > 0 ? "\u300C".concat(attrNames.join(','), "\u300D").concat(message) : message, - }); - throw err_4; - } - } - this.setMessage({ - type: 'error', - content: err_4.message, - }); - throw err_4; - case 5: return [2 /*return*/]; } }); }); @@ -421,62 +391,6 @@ function callPicker(attr, params) { }); } exports.callPicker = callPicker; -function setUpdateData(attr, data) { - return tslib_1.__awaiter(this, void 0, void 0, function () { - var _a, _b; - return tslib_1.__generator(this, function (_c) { - switch (_c.label) { - case 0: - (0, assert_1.assert)(attr.indexOf('.') === -1, 'setUpdateData只能设置当前对象属性,子层对象请写完整的addOperation'); - if (!this.props.oakId) return [3 /*break*/, 1]; - return [2 /*return*/, this.addOperation({ - action: 'update', - data: (_a = {}, - _a[attr] = data, - _a) - })]; - case 1: return [4 /*yield*/, this.addOperation({ - action: 'create', - data: (_b = {}, - _b[attr] = data, - _b) - })]; - case 2: - _c.sent(); - _c.label = 3; - case 3: return [2 /*return*/]; - } - }); - }); -} -exports.setUpdateData = setUpdateData; -function setMultiAttrUpdateData(data) { - return tslib_1.__awaiter(this, void 0, void 0, function () { - var key; - return tslib_1.__generator(this, function (_a) { - switch (_a.label) { - case 0: - for (key in data) { - (0, assert_1.assert)(key.indexOf('.') === -1, 'setMultiAttrUpdateData只能设置当前对象属性,子层对象请写完整的addOperation'); - } - if (!this.props.oakId) return [3 /*break*/, 1]; - return [2 /*return*/, this.addOperation({ - action: 'update', - data: data, - })]; - case 1: return [4 /*yield*/, this.addOperation({ - action: 'create', - data: data, - })]; - case 2: - _a.sent(); - _a.label = 3; - case 3: return [2 /*return*/]; - } - }); - }); -} -exports.setMultiAttrUpdateData = setMultiAttrUpdateData; function destroyNode() { (0, assert_1.assert)(this.state.oakFullpath); this.features.runningTree.destroyNode(this.state.oakFullpath); diff --git a/lib/page.web.js b/lib/page.web.js index df40c5bc..afe4f4a2 100644 --- a/lib/page.web.js +++ b/lib/page.web.js @@ -21,7 +21,7 @@ var OakComponentBase = /** @class */ (function (_super) { page_common_1.unsubscribe.call(this); }; OakComponentBase.prototype.onPathSet = function () { - return page_common_1.onPathSet.call(this, this.option); + return page_common_1.onPathSet.call(this, this.oakOption); }; OakComponentBase.prototype.triggerEvent = function (name, detail, options) { }; @@ -77,7 +77,7 @@ var OakComponentBase = /** @class */ (function (_super) { return this.features.message.consumeMessage(); }; OakComponentBase.prototype.reRender = function (extra) { - return page_common_1.reRender.call(this, this.option, extra); + return page_common_1.reRender.call(this, this.oakOption, extra); }; OakComponentBase.prototype.navigateTo = function (options, state, disableNamespace) { var url = options.url, rest = tslib_1.__rest(options, ["url"]); @@ -184,11 +184,28 @@ var OakComponentBase = /** @class */ (function (_super) { return this.props.navigate(url2, { replace: true }); } } */ - OakComponentBase.prototype.addOperation = function (operation, beforeExecute, afterExecute, path) { - var path2 = path ? "".concat(this.state.oakFullpath, ".").concat(path) : this.state.oakFullpath; - return this.features.runningTree.addOperation(path2, operation, beforeExecute, afterExecute); + OakComponentBase.prototype.addItem = function (data, beforeExecute, afterExecute) { + return this.features.runningTree.addItem(this.state.oakFullpath, data, beforeExecute, afterExecute); }; - OakComponentBase.prototype.cleanOperation = function (path) { + OakComponentBase.prototype.removeItem = function (id, beforeExecute, afterExecute) { + return this.features.runningTree.removeItem(this.state.oakFullpath, id, beforeExecute, afterExecute); + }; + OakComponentBase.prototype.updateItem = function (data, id, action, beforeExecute, afterExecute) { + return this.features.runningTree.updateItem(this.state.oakFullpath, data, id, action, beforeExecute, afterExecute); + }; + OakComponentBase.prototype.recoverItem = function (id) { + return this.features.runningTree.recoverItem(this.state.oakFullpath, id); + }; + OakComponentBase.prototype.create = function (data, beforeExecute, afterExecute) { + return this.features.runningTree.create(this.state.oakFullpath, data, beforeExecute, afterExecute); + }; + OakComponentBase.prototype.update = function (data, action, beforeExecute, afterExecute) { + return this.features.runningTree.update(this.state.oakFullpath, data, action, beforeExecute, afterExecute); + }; + OakComponentBase.prototype.remove = function (beforeExecute, afterExecute) { + return this.features.runningTree.remove(this.state.oakFullpath, beforeExecute, afterExecute); + }; + OakComponentBase.prototype.clean = function (path) { var path2 = path ? "".concat(this.state.oakFullpath, ".").concat(path) : this.state.oakFullpath; return this.features.runningTree.clean(path2); }; @@ -199,8 +216,8 @@ var OakComponentBase = /** @class */ (function (_super) { if (params === void 0) { params = {}; } return page_common_1.callPicker.call(this, attr, params); }; - OakComponentBase.prototype.execute = function (operation) { - return page_common_1.execute.call(this, operation); + OakComponentBase.prototype.execute = function (data) { + return page_common_1.execute.call(this, data); }; OakComponentBase.prototype.getFreshValue = function (path) { var path2 = path ? "".concat(this.state.oakFullpath, ".").concat(path) : this.state.oakFullpath; @@ -220,12 +237,6 @@ var OakComponentBase = /** @class */ (function (_super) { OakComponentBase.prototype.refresh = function () { return page_common_1.refresh.call(this); }; - OakComponentBase.prototype.setUpdateData = function (attr, data) { - return page_common_1.setUpdateData.call(this, attr, data); - }; - OakComponentBase.prototype.setMultiAttrUpdateData = function (data) { - return page_common_1.setMultiAttrUpdateData.call(this, data); - }; OakComponentBase.prototype.loadMore = function () { return page_common_1.loadMore.call(this); }; @@ -477,34 +488,141 @@ function translateObservers(observers) { } var DEFAULT_REACH_BOTTOM_DISTANCE = 50; function createComponent(option, features) { - var _a = option, data = _a.data, projection = _a.projection, properties = _a.properties, entity = _a.entity, methods = _a.methods, lifetimes = _a.lifetimes, observers = _a.observers, render = _a.render, path = _a.path; + var _a = option, data = _a.data, methods = _a.methods, lifetimes = _a.lifetimes, observers = _a.observers, getRender = _a.getRender, path = _a.path; var fn = translateObservers(observers).fn; var OakComponentWrapper = /** @class */ (function (_super) { tslib_1.__extends(OakComponentWrapper, _super); function OakComponentWrapper(props) { - var _a; var _this = _super.call(this, props) || this; _this.features = features; - _this.option = option; + _this.oakOption = option; _this.isReachBottom = false; _this.scrollEvent = function () { _this.checkReachBottom(); }; + var methodProps = { + t: function (key, params) { return _this.t(key, params); }, + execute: function (data) { + return _this.execute(data); + }, + refresh: function () { + return _this.refresh(); + }, + setNotification: function (data) { + return _this.setNotification(data); + }, + setMessage: function (data) { + return _this.setMessage(data); + }, + navigateTo: function (options, state, disableNamespace) { + return _this.navigateTo(options, state, disableNamespace); + }, + navigateBack: function (options) { + return _this.navigateBack(options); + }, + redirectTo: function (options, state, disableNamespace) { + return _this.redirectTo(options, state, disableNamespace); + }, + clean: function (path) { + return _this.clean(path); + } + }; + if (option.isList) { + Object.assign(methodProps, { + addItem: function (data, beforeExecute, afterExecute) { + return _this.addItem(data, beforeExecute, afterExecute); + }, + removeItem: function (id, beforeExecute, afterExecute) { + return _this.removeItem(id, beforeExecute, afterExecute); + }, + updateItem: function (data, id, action, beforeExecute, afterExecute) { + return _this.updateItem(data, id, action, beforeExecute, afterExecute); + }, + setFilters: function (filters) { + return _this.setFilters(filters); + }, + addNamedFilter: function (filter, refresh) { + return _this.addNamedFilter(filter, refresh); + }, + removeNamedFilter: function (filter, refresh) { + return _this.removeNamedFilter(filter, refresh); + }, + removeNamedFilterByName: function (name, refresh) { + return _this.removeNamedFilterByName(name, refresh); + }, + setNamedSorters: function (sorters) { + return _this.setNamedSorters(sorters); + }, + addNamedSorter: function (sorter, refresh) { + return _this.addNamedSorter(sorter, refresh); + }, + removeNamedSorter: function (sorter, refresh) { + return _this.removeNamedSorter(sorter, refresh); + }, + removeNamedSorterByName: function (name, refresh) { + return _this.removeNamedSorterByName(name, refresh); + }, + setPageSize: function (pageSize) { + return _this.setPageSize(pageSize); + }, + setCurrentPage: function (current) { + return _this.setCurrentPage(current); + }, + loadMore: function () { + return _this.loadMore(); + } + }); + } + else { + Object.assign(methodProps, { + create: function (data, beforeExecute, afterExecute) { + return _this.create(data, beforeExecute, afterExecute); + }, + update: function (data, action, beforeExecute, afterExecute) { + return _this.update(data, action, beforeExecute, afterExecute); + }, + remove: function (beforeExecute, afterExecute) { + return _this.remove(beforeExecute, afterExecute); + }, + }); + } if (methods) { - for (var m in methods) { - Object.assign(_this, (_a = {}, - _a[m] = methods[m].bind(_this), + var _loop_1 = function (m) { + var _a, _b; + Object.assign(this_1, (_a = {}, + _a[m] = function () { + var _a; + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return (_a = methods[m]).call.apply(_a, tslib_1.__spreadArray([_this], tslib_1.__read(args), false)); + }, _a)); + Object.assign(methodProps, (_b = {}, + _b[m] = function () { + var _a; + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return (_a = methods[m]).call.apply(_a, tslib_1.__spreadArray([_this], tslib_1.__read(args), false)); + }, + _b)); + }; + var this_1 = this; + for (var m in methods) { + _loop_1(m); } } _this.state = Object.assign({}, data, { oakLoading: false, oakLoadingMore: false, oakPullDownRefreshLoading: false, - oakIsReady: false, oakExecuting: false, oakDirty: false, }); + _this.methodProps = methodProps; (lifetimes === null || lifetimes === void 0 ? void 0 : lifetimes.created) && lifetimes.created.call(_this); return _this; } @@ -596,9 +714,9 @@ function createComponent(option, features) { }; OakComponentWrapper.prototype.render = function () { var _this = this; - var Render = render.call(this); var oakPullDownRefreshLoading = this.state.oakPullDownRefreshLoading; var _a = this.props.oakDisablePulldownRefresh, oakDisablePulldownRefresh = _a === void 0 ? false : _a; + var Render = getRender.call(this); if (this.supportPullDownRefresh() && !oakDisablePulldownRefresh) { var Child = react_1.default.cloneElement((0, jsx_runtime_1.jsx)(web_1.PullToRefresh, { onRefresh: function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) { @@ -619,10 +737,10 @@ function createComponent(option, features) { finish: this.t('common:ptrFinish'), } }), { getScrollContainer: function () { return document.body; }, - }, Render); + }, (0, jsx_runtime_1.jsx)(Render, { methods: this.methodProps, data: tslib_1.__assign(tslib_1.__assign({}, this.state), this.props) })); return Child; } - return Render; + return (0, jsx_runtime_1.jsx)(Render, { methods: this.methodProps, data: tslib_1.__assign(tslib_1.__assign({}, this.state), this.props) }); }; return OakComponentWrapper; }(OakComponentBase)); diff --git a/lib/types/Page.d.ts b/lib/types/Page.d.ts index 69bff882..eb04c06c 100644 --- a/lib/types/Page.d.ts +++ b/lib/types/Page.d.ts @@ -58,14 +58,15 @@ export declare type ComponentPublicThisType; setState: (data: Partial>, callback?: () => void) => void; triggerEvent: (name: string, detail?: DetailType, options?: WechatMiniprogram.Component.TriggerEventOption) => void; -} & TMethod & OakCommonComponentMethods & (IsList extends true ? OakListComponentMethods : {}); -export declare type ComponentFullThisType> = { +} & TMethod & OakCommonComponentMethods & (IsList extends true ? OakListComponentMethods : OakSingleComponentMethods); +export declare type ComponentFullThisType> = { + subscribed?: () => 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 & OakHiddenComponentMethods; +} & OakCommonComponentMethods & (IsList extends true ? OakListComponentMethods : OakSingleComponentMethods); export declare type OakComponentOption, AD extends Record>, FD extends Record, Proj extends ED[T]['Selection']['data'], FormedData extends Record, IsList extends boolean, TData extends Record, TProperty extends WechatMiniprogram.Component.PropertyOption, TMethod extends Record> = ComponentOption & Partial<{ data?: TData; properties: TProperty; @@ -146,20 +147,22 @@ export declare type OakCommonComponentMethods Promise; redirectTo: (options: Parameters[0] & OakNavigateToParameters, state?: Record, disableNamespace?: boolean) => Promise; - addOperation: (operation: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise, path?: string) => Promise; - cleanOperation: (path?: string) => void; + clean: (path?: string) => void; t(key: string, params?: object): string; callPicker: (attr: string, params: Record) => void; - execute: (operation?: Omit, path?: string) => Promise; + execute: (data?: ED[T]['Update']['data'] | Record, path?: string) => Promise; checkOperation: (ntity: T, action: ED[T]['Action'], filter?: ED[T]['Update']['filter'], checkerTypes?: CheckerType[]) => Promise; tryExecute: (path?: string) => Promise; getOperations: (path?: string) => Promise; - refresh: (extra?: any) => Promise; - setUpdateData: (data: string, attr: any) => Promise; - setMultiAttrUpdateData: (data: Record) => Promise; + refresh: () => Promise; +}; +export declare type OakSingleComponentMethods = { setId: (id: string) => Promise; unsetId: () => void; getId: () => string | undefined; + create: (data: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise) => Promise; + update: (data: ED[T]['Update']['data'], action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise) => Promise; + remove: (beforeExecute?: () => Promise, afterExecute?: () => Promise) => Promise; }; export declare type OakListComponentMethods = { loadMore: () => Promise; @@ -178,6 +181,9 @@ export declare type OakListComponentMethods Pagination | undefined; setPageSize: (pageSize: number) => void; setCurrentPage: (current: number) => void; + addItem: (data: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise) => Promise; + removeItem: (id: string, beforeExecute?: () => Promise, afterExecute?: () => Promise) => Promise; + updateItem: (data: ED[T]['Update']['data'], id: string, action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise) => Promise; }; declare type ComponentOnPropsChangeOption = { path?: string; @@ -200,9 +206,15 @@ export declare type OakComponentData, AD extends Record>, FD extends Record> = (options: OakComponentOption) => React.ComponentType; +export declare type WebComponentCommonMethodNames = 'setNotification' | 'setMessage' | 'navigateTo' | 'navigateBack' | 'redirectTo' | 'clean' | 't' | 'execute' | 'refresh'; +export declare type WebComponentListMethodNames = 'loadMore' | 'setFilters' | 'addNamedFilter' | 'removeNamedFilter' | 'removeNamedFilterByName' | 'setNamedSorters' | 'addNamedSorter' | 'removeNamedSorter' | 'removeNamedSorterByName' | 'setPageSize' | 'setCurrentPage' | 'addItem' | 'removeItem' | 'updateItem'; +export declare type WebComponentSingleMethodNames = 'create' | 'update' | 'remove'; +export declare type WebComponentProps = { + methods: TMethod & Pick, WebComponentCommonMethodNames> & (IsList extends true ? Pick, WebComponentListMethodNames> : Pick, WebComponentSingleMethodNames>); + data: TData & OakComponentData & (IsList extends true ? OakListComponentProperties : {}); +}; export {}; diff --git a/src/cacheStore/CacheStore.ts b/src/cacheStore/CacheStore.ts index 92ce402a..4a4b714e 100644 --- a/src/cacheStore/CacheStore.ts +++ b/src/cacheStore/CacheStore.ts @@ -6,9 +6,7 @@ import { Checker, CheckerType, Context, Trigger } from 'oak-domain/lib/types'; import { TreeStore, TreeStoreOperateOption } from 'oak-memory-tree-store'; import assert from 'assert'; -interface CachStoreOperation extends TreeStoreOperateOption { - inSync?: boolean; -}; +interface CachStoreOperation extends TreeStoreOperateOption {}; export class CacheStore< ED extends EntityDict & BaseEntityDict, @@ -32,24 +30,6 @@ export class CacheStore< this.resetInitialDataFn = resetInitialDataFn; } - protected async updateAbjointRow(entity: T, operation: DeduceCreateSingleOperation | DeduceUpdateOperation | DeduceRemoveOperation, context: Cxt, option?: CachStoreOperation): Promise { - if (!option?.inSync) { - // 如果不是同步,需要补齐所有的null属性 - const { action, data } = operation; - if (action === 'create') { - const { attributes } = this.getSchema()[entity]; - for (const key in attributes) { - if (data[key] === undefined) { - Object.assign(data, { - [key]: null, - }); - } - } - } - } - return super.updateAbjointRow(entity, operation, context, option); - } - async operate( entity: T, operation: ED[T]['Operation'], @@ -89,9 +69,7 @@ export class CacheStore< let result; try { - result = await super.sync(opRecords, context, { - inSync: true, - }); + result = await super.sync(opRecords, context, {}); } catch (err) { if (autoCommit) { await context.rollback(); diff --git a/src/debugStore/DebugStore.ts b/src/debugStore/DebugStore.ts index 00c90b7f..b2ac6dae 100644 --- a/src/debugStore/DebugStore.ts +++ b/src/debugStore/DebugStore.ts @@ -22,26 +22,6 @@ export class DebugStore( - entity: T, - operation: ED[T]['CreateSingle'] | ED[T]['Update'] | ED[T]['Remove'], - context: Cxt, - option?: OP) { - // 对于create动作,没有值的属性要置NULL - const { action, data } = operation; - if (action === 'create') { - const { attributes } = this.getSchema()[entity]; - for (const key in attributes) { - if (data[key] === undefined) { - Object.assign(data, { - [key]: null, - }); - } - } - } - return super.updateAbjointRow(entity, operation, context, option); - } - protected async cascadeUpdate(entity: T, operation: DeduceCreateOperation | DeduceUpdateOperation | DeduceRemoveOperation, context: Cxt, option: OP) { if (!option.blockTrigger) { await this.executor.preOperation(entity, operation, context, option); diff --git a/src/features/runningTree.ts b/src/features/runningTree.ts index fede5ba5..1c56884b 100644 --- a/src/features/runningTree.ts +++ b/src/features/runningTree.ts @@ -23,7 +23,7 @@ abstract class Node; protected projection: ED[T]['Selection']['data'] | (() => Promise); // 只在Page层有 - protected parent?: Node | VirtualNode; + protected parent?: SingleNode | ListNode | VirtualNode; protected dirty?: boolean; protected cache: Cache; protected loading: boolean; @@ -34,7 +34,7 @@ abstract class Node, cache: Cache, projection: ED[T]['Selection']['data'] | (() => Promise), - parent?: Node | VirtualNode) { + parent?: SingleNode | ListNode | VirtualNode) { this.entity = entity; this.schema = schema; this.cache = cache; @@ -44,7 +44,7 @@ abstract class Node Promise), - parent?: Node | VirtualNode, + parent?: SingleNode | VirtualNode, filters?: NamedFilterItem[], sorters?: NamedSorterItem[], pagination?: Pagination @@ -783,7 +783,7 @@ class ListNode< if (operation.action === 'create') { const { data } = operation; assert(!(data instanceof Array)); - createdIds.push(data.id); + createdIds.push(data.id); } } @@ -818,15 +818,14 @@ class ListNode< filter: combineFilters([filter, { id: { $in: createdIds } }].filter(ele => !!ele), true), }); } - + const result = await this.cache.tryRedoOperationsThenSelect(this.entity, selection, operations); return result; } return []; } - async addItem(item: Omit, beforeExecute: () => Promise, afterExecute: () => Promise) { - assert(Object.keys(this.children).length === 0, ``); + async addItem(item: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise) { const id = await generateNewId(); assert(!this.updates[id]); this.updates[id] = { @@ -841,19 +840,25 @@ class ListNode< this.setDirty(); } - async removeItem(id: string, beforeExecute: () => Promise, afterExecute: () => Promise) { - this.updates[id] = { - beforeExecute, - afterExecute, - operation: { - id: await generateNewId(), - action: 'remove', - data: {}, - filter: { - id, - }, - } as ED[T]['Remove'], - }; + async removeItem(id: string, beforeExecute?: () => Promise, afterExecute?: () => Promise) { + if (this.updates[id] && this.updates[id].operation.action === 'create') { + // 如果是新增项,在这里抵消 + unset(this.updates, id); + } + else { + this.updates[id] = { + beforeExecute, + afterExecute, + operation: { + id: await generateNewId(), + action: 'remove', + data: {}, + filter: { + id, + }, + } as ED[T]['Remove'], + }; + } this.setDirty(); } @@ -863,11 +868,23 @@ class ListNode< unset(this.updates, id); } - async updateItem(data: ED[T]['Update']['data'], id: string, beforeExecute: () => Promise, afterExecute: () => Promise) { + /** + * 目前只支持根据itemId进行更新 + * @param data + * @param id + * @param beforeExecute + * @param afterExecute + */ + async updateItem(data: ED[T]['Update']['data'], id: string, action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise) { + assert(Object.keys(this.children).length === 0, `更新子结点应该落在相应的component上`); if (this.updates[id]) { const { operation } = this.updates[id]; const { data: dataOrigin } = operation; merge(dataOrigin, data); + if (action && operation.action !== action) { + assert(operation.action === 'update'); + operation.action = action; + } } else { this.updates[id] = { @@ -875,7 +892,7 @@ class ListNode< afterExecute, operation: { id: await generateNewId(), - action: 'update', + action: action || 'update', data, filter: { id, @@ -886,6 +903,12 @@ class ListNode< this.setDirty(); } + async updateItems(data: Record, action?: ED[T]['Action']) { + for (const id in data) { + await this.updateItem(data[id], id, action); + } + } + async doBeforeTrigger(): Promise { for (const k in this.updates) { const update = this.updates[k]; @@ -913,16 +936,7 @@ class ListNode< } async getParentFilter(childNode: SingleNode): Promise { - /* let idx = 0; - while (idx < this.ids!.length) { - if (this.children[idx] === childNode) { - return { - id: this.ids![idx], - } - } - idx++; - } */ - for(const id in this.children) { + for (const id in this.children) { if (this.children[id] === childNode) { return { id, @@ -935,52 +949,32 @@ class ListNode< if (!this.dirty) { return; } - const childOperations = await Promise.all( - Object.keys(this.children).map( - async ele => { - const child = this.children[ele]; - const childOperations = await child.composeOperations(); - if (childOperations) { - assert(childOperations.length === 1); - return childOperations[0]; - } - } - ) - ); - - const operations: ED[T]['Operation'][] = cloneDeep(this.operations.map(ele => ele.oper)); - for (const oper of childOperations) { - if (oper) { - const { - index, - eliminated, - } = findOperationToMerge(this.entity, this.schema, oper, operations); - if (index) { - // 可以合并 - const result = mergeOperationOper(this.entity, this.schema, oper, index); - if (result) { - // 说明相互抵消了 - pull(operations, index); - } - else { - } - } - else { - operations.push(oper); - } - - - for (const eli of eliminated) { - if (eli) { - pull(operations, eli); - } - } + const childOperations: Record = {}; + for (const id in this.children) { + const childOperation = await this.children[id].composeOperations(); + if (childOperation) { + assert(childOperation.length === 1); + childOperations[id] = childOperation[0].operation; } } + const operations: ED[T]['Operation'][] = []; + for (const id in this.updates) { + const operation = cloneDeep(this.updates[id].operation); + if (childOperations[id]) { + const childOperation = childOperations[id]; + // 在list有operation在singleNode上也有目前只允许一种情况,即list上create,在single上update + assert(operation.action === 'create' && childOperation.action === 'update'); + Object.assign(operation.data, childOperation.data); + unset(childOperations, id); + } + operations.push(operation); + } + operations.push(...Object.values(childOperations)); await repairOperations(this.entity, this.schema, operations); return operations.map( - ele => Object.assign(ele, { + ele => Object.assign({ + operation: ele, entity: this.entity, }) ); @@ -1078,7 +1072,7 @@ class ListNode< if (getCount) { this.pagination.total = count; } - + const ids = data.map( ele => ele.id! ) as string[]; @@ -1115,7 +1109,7 @@ class ListNode< clean() { this.dirty = undefined; - this.operations = []; + this.updates = {}; for (const k in this.children) { this.children[k].clean(); @@ -1131,15 +1125,38 @@ class SingleNode | ListNode; }; + private operation?: { + beforeExecute?: () => Promise; + afterExecute?: () => Promise; + operation: ED[T]['CreateSingle'] | ED[T]['Update'] | ED[T]['Remove']; + }; constructor(entity: T, schema: StorageSchema, cache: Cache, projection: ED[T]['Selection']['data'] | (() => Promise), - parent?: Node | VirtualNode, id?: string) { + parent?: SingleNode | ListNode | VirtualNode, id?: string) { super(entity, schema, cache, projection, parent); this.children = {}; if (id) { this.id = id; } + else { + // 若没有父结点上的filter,则一定是create动作 + this.tryGetParentFilter() + .then( + () => this.create({}) + ); + } + } + + private async tryGetParentFilter(disableOperation?: boolean) { + const parent = this.getParent(); + if (parent instanceof SingleNode) { + const filter = await parent.getParentFilter(this, disableOperation); + return filter; + } + else if (parent instanceof ListNode) { + return await parent.getParentFilter(this); + } } protected getChildPath(child: Node): string { @@ -1159,7 +1176,7 @@ class SingleNode 0) { + if (this.operation) { return; } for (const k in this.children) { @@ -1219,33 +1236,17 @@ class SingleNode : []; let filter = this.id && { id: this.id } as ED[T]['Selection']['filter']; - if (!filter && !disableOperation) { - // 可能是create - const createOper = this.operations.find( - (ele) => ele.oper.action === 'create' - ) as { oper: ED[T]['CreateSingle'] }; - if (createOper) { - assert(createOper.oper.data.id); - filter = { - id: createOper.oper.data.id - }; - } - } + if (!filter) { // 还可能是来自父级的外键 - const parent = this.getParent(); - if (parent instanceof ListNode || parent instanceof SingleNode) { - filter = await parent.getParentFilter(this, disableOperation); - } + filter = await this.tryGetParentFilter(disableOperation); } if (filter) { - if (!disableOperation) { - operations.push(...this.operations.map( - ele => ({ - entity: this.entity, - operation: ele.oper, - }) - )); + if (!disableOperation && this.operation) { + operations.push({ + entity: this.entity, + operation: this.operation.operation, + }); } const result = await this.cache.tryRedoOperationsThenSelect(this.entity, { data: projection, @@ -1257,10 +1258,8 @@ class SingleNode { - for (const operation of this.operations) { - if (operation.beforeExecute) { - await operation.beforeExecute(); - } + if (this.operation?.beforeExecute) { + await this.operation.beforeExecute(); } for (const k in this.children) { @@ -1269,12 +1268,9 @@ class SingleNode { - for (const operation of this.operations) { - if (operation.afterExecute) { - await operation.afterExecute(); - } + if (this.operation?.afterExecute) { + await this.operation.afterExecute(); } for (const k in this.children) { @@ -1283,133 +1279,125 @@ class SingleNode, beforeExecute?: Operation['beforeExecute'], afterExecute?: Operation['afterExecute']) { - if (this.id) { - if (oper.action === 'create') { - oper.action = 'update'; - } - if (!oper.filter) { - Object.assign(oper, { + async create(data: Partial>, beforeExecute?: () => Promise, afterExecute?: () => Promise) { + const id = await generateNewId(); + this.id = id; + this.operation = { + operation: { + id: await generateNewId(), + action: 'create', + data: Object.assign({}, data, { id }), + }, + beforeExecute, + afterExecute, + }; + this.setDirty(); + } + + async update(data: ED[T]['Update']['data'], action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise) { + if (!this.operation) { + const operation: ED[T]['Update'] = { + id: await generateNewId(), + action: action || 'update', + data, + }; + if (this.id) { + Object.assign(operation, { filter: { id: this.id, }, }); } - else { - assert(oper.filter.id === this.id); - } - } - const operation = { - oper, - beforeExecute, - afterExecute, - }; - if (this.operations.length === 0) { - // 处理一下create - if (oper.action === 'create') { - const id = await generateNewId(); - Object.assign(oper.data, { - id, - }); - } - Object.assign(oper, { id: await generateNewId() }); - this.operations.push(operation as Operation); + this.operation = { + operation, + beforeExecute, + afterExecute, + }; } else { - // singleNode上应当有且只有一个operation,无论什么情况 - const result = mergeOperationOper(this.entity, this.schema, oper, this.operations[0].oper); - assert(!result); + const { operation } = this.operation; + assert(['create', 'update', action].includes(operation.action)); + Object.assign(operation.data, data); + if (action && operation.action !== action) { + operation.action = action; + } } this.setDirty(); } - async composeOperations() { + async remove(beforeExecute?: () => Promise, afterExecute?: () => Promise) { + const operation: ED[T]['Remove'] = { + id: await generateNewId(), + action: 'remove', + data: {}, + }; + if (this.id) { + Object.assign(operation, { + filter: { + id: this.id, + }, + }); + } + this.operation = { + operation, + beforeExecute, + afterExecute, + }; + this.setDirty(); + } + + async composeOperations(): Promise | undefined> { if (!this.dirty) { return; } - const childOperations = await Promise.all( - Object.keys(this.children).map( - async (ele) => { - const child = this.children[ele]; - const childOperations = await child!.composeOperations(); - let subOper; - if (childOperations) { - if (child instanceof SingleNode) { - subOper = childOperations[0]; - } - else { - assert(child instanceof ListNode); - subOper = childOperations; - } - } + let operation: ED[T]['Update'] = this.operation ? cloneDeep(this.operation.operation) : { + id: await generateNewId(), + action: 'update', + data: {}, + }; // 如果本结点是create,在初始化时就应该置上operation + if (this.id && !operation.filter) { + Object.assign(operation, { + filter: { + id: this.id, + } + }); + } - if (subOper) { - const sliceIdx = ele.indexOf(':'); - const ele2 = sliceIdx > 0 ? ele.slice(0, sliceIdx) : ele; - if (this.id) { - return { - id: 'dummy', // 因为肯定会被merge掉,所以无所谓了 - action: 'update', - data: { - [ele2]: subOper, - }, - filter: { - id: this.id, - } - } as ED[T]['Operation']; - } - else { - return { - id: 'dummy', // 因为肯定会被merge掉,所以无所谓了 - action: 'create', - data: { - [ele2]: subOper, - }, - } as ED[T]['Operation']; - } + for (const ele in this.children) { + const child = this.children[ele]; + const childOperations = await child!.composeOperations(); + const sliceIdx = ele.indexOf(':'); + const ele2 = sliceIdx > 0 ? ele.slice(0, sliceIdx) : ele; + if (childOperations) { + if (child instanceof SingleNode) { + assert(childOperations.length === 1); + assert(!operation.data[ele2]); // 多对一的子结点不应该有多项 + Object.assign(operation.data, { + [ele2]: childOperations[0].operation, + }); + } + else { + assert(child instanceof ListNode); + const childOpers = childOperations.map( + ele => ele.operation + ); + if (operation.data[ele2]) { + operation.data[ele2].push(...childOpers); + } + else { + operation.data[ele2] = childOpers; } } - ) - ); + } + } - const operations: ED[T]['Operation'][] = []; - if (this.operations.length > 0) { - assert(this.operations.length === 1); - // 这里不能直接改this.operations,只能克隆一个新的 - operations.push(cloneDeep(this.operations[0].oper)); - } - else { - if (this.id) { - operations.push({ - id: await generateNewId(), - action: 'update', - data: {}, - filter: { - id: this.id, - } as any, - }); - } - else { - operations.push({ - id: await generateNewId(), - action: 'create', - data: { - id: await generateNewId(), - }, - }); - } - } - for (const oper of childOperations) { - if (oper) { - mergeOperationOper(this.entity, this.schema, oper, operations[0]); // SingleNode貌似不可能不merge成功 - } - } - await repairOperations(this.entity, this.schema, operations); - return operations.map( - ele => Object.assign(ele, { - entity: this.entity, - }) - ); + return [{ + entity: this.entity, + operation, + }]; } async getProjection(withDecendants?: boolean) { @@ -1455,8 +1443,10 @@ class SingleNode(childNode: Node, disableOperation?: boolean): Promise { + async getParentFilter(childNode: Node, disableOperation?: boolean): Promise { const value = (await this.getFreshValue(disableOperation))!; if (!value) { return; @@ -1729,7 +1719,7 @@ export class RunningTree< ED extends EntityDict & BaseEntityDict, Cxt extends Context, AD extends CommonAspectDict - > extends Feature { + > extends Feature { private cache: Cache; private schema: StorageSchema; private root: Record< @@ -1769,7 +1759,7 @@ export class RunningTree< // 目前只有一种情况合法,即parentNode是list,列表中的位置移动引起的重用 if (parentNode instanceof ListNode) { } - else if (process.env.NODE_ENV === 'development') { + else if (process.env.NODE_ENV === 'development') { console.error(`创建node时发现已有结点,不能重用。「${fullPath}」`); } return; @@ -1777,6 +1767,7 @@ export class RunningTree< if (entity) { if (isList) { + assert(!(parentNode instanceof ListNode)); node = new ListNode( entity, this.schema!, @@ -1793,7 +1784,7 @@ export class RunningTree< this.schema!, this.cache, projection!, - parentNode, + parentNode as VirtualNode, // 过编译 id ); } @@ -1860,11 +1851,52 @@ export class RunningTree< return node ? node.isDirty() : false; } - @Action - async addOperation(path: string, operation: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise) { + async addItem(path: string, data: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise) { const node = this.findNode(path); - assert(node && (node instanceof SingleNode || node instanceof ListNode)); - await node.addOperation(operation, beforeExecute, afterExecute); + assert(node instanceof ListNode); + await node.addItem(data, beforeExecute, afterExecute); + } + + @Action + async removeItem(path: string, id: string, beforeExecute?: () => Promise, afterExecute?: () => Promise) { + const node = this.findNode(path); + assert(node instanceof ListNode); + await node.removeItem(id, beforeExecute, afterExecute); + } + + @Action + async updateItem(path: string, data: ED[T]['Update']['data'], id: string, action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise) { + const node = this.findNode(path); + assert(node instanceof ListNode); + await node.updateItem(data, id, action, beforeExecute, afterExecute); + } + + @Action + async recoverItem(path: string, id: string) { + const node = this.findNode(path); + assert(node instanceof ListNode); + await node.recoverItem(id); + } + + @Action + async create(path: string, data: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise) { + const node = this.findNode(path); + assert(node instanceof SingleNode); + await node.create(data, beforeExecute, afterExecute); + } + + @Action + async update(path: string, data: ED[T]['Update']['data'], action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise) { + const node = this.findNode(path); + assert (node instanceof SingleNode); + await node.update(data, action, beforeExecute, afterExecute); + } + + @Action + async remove(path: string, beforeExecute?: () => Promise, afterExecute?: () => Promise) { + const node = this.findNode(path); + assert(node instanceof SingleNode); + await node.remove(beforeExecute, afterExecute); } isLoading(path: string) { @@ -1904,7 +1936,7 @@ export class RunningTree< await node.loadMore(); } - getPagination(path: string) { + getPagination(path: string) { const node = this.findNode(path); assert(node instanceof ListNode); return node.getPagination(); @@ -2054,7 +2086,7 @@ export class RunningTree< } @Action - async removeNamedSorterByName( + async removeNamedSorterByName( path: string, name: string, refresh: boolean = false @@ -2080,11 +2112,16 @@ export class RunningTree< } @Action - async execute(path: string, operation?: Omit) { + async execute(path: string, data?: ED[T]['Update']['data'] | Record, action?: ED[T]['Action']) { const node = this.findNode(path)!; - if (operation) { - assert(node instanceof ListNode || node instanceof SingleNode); - await node.addOperation(operation); + if (data) { + if(node instanceof SingleNode) { + await node.update(data, action); + } + else { + assert(node instanceof ListNode); + node.updateItems(data); + } } assert(node.isDirty()); @@ -2093,7 +2130,7 @@ export class RunningTree< await node.doBeforeTrigger(); const operations = (await node.composeOperations())!; - // 这里理论上virtualNode下面可以有多个不同的entity的组件,但实际中不应当出现这样的设计 + // 这里理论上virtualNode下面也可以有多个不同的entity的组件,但实际中不应当出现这样的设计 const entities = uniq( operations.filter(ele => !!ele).map( ele => ele.entity @@ -2102,7 +2139,7 @@ export class RunningTree< await this.aspectWrapper.exec('operate', { entity: entities[0], - operation: operations.filter(ele => !!ele), + operation: operations.filter(ele => !!ele).map(ele => ele.operation), }); await node.doAfterTrigger(); diff --git a/src/index.ts b/src/index.ts index f7633a07..d6a197b1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,4 +7,5 @@ export * from './features/localStorage'; export * from './utils/upload'; export * from './types/Notification'; export * from './types/Message'; -export * from './utils/bluetooth'; \ No newline at end of file +export * from './utils/bluetooth'; +export * from './types/Page'; \ No newline at end of file diff --git a/src/page.common.ts b/src/page.common.ts index 569f3238..17678000 100644 --- a/src/page.common.ts +++ b/src/page.common.ts @@ -18,7 +18,7 @@ import { unset } from 'oak-domain/lib/utils/lodash'; export function subscribe< ED extends EntityDict & BaseEntityDict, T extends keyof ED, - Cxt extends Context>(this: ComponentFullThisType): void { + Cxt extends Context>(this: ComponentFullThisType): void { if (!this.subscribed) { this.subscribed = FeactureSubscribe(() => this.reRender()); } @@ -27,7 +27,7 @@ export function subscribe< export function unsubscribe< ED extends EntityDict & BaseEntityDict, T extends keyof ED, - Cxt extends Context>(this: ComponentFullThisType): void { + Cxt extends Context>(this: ComponentFullThisType): void { if (this.subscribed) { this.subscribed(); this.subscribed = undefined; @@ -38,7 +38,7 @@ export async function onPathSet< ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends Context>( - this: ComponentFullThisType, + this: ComponentFullThisType, option: OakComponentOption) { const { props, state } = this; const { oakPath, oakProjection, oakIsPicker, oakFilters, oakSorters, oakId } = props; @@ -142,7 +142,7 @@ export async function reRender< ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends Context>( - this: ComponentFullThisType, + this: ComponentFullThisType, option: OakComponentOption, extra?: Record) { const { features } = this; @@ -224,7 +224,7 @@ export async function reRender< }); this.setState(data); - } else if (this.state.oakFullpath) { + } else { const data: Record = formData ? await formData.call(this, { features, @@ -244,7 +244,7 @@ export async function refresh< ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends Context>( - this: ComponentFullThisType + this: ComponentFullThisType ) { if (this.state.oakFullpath) { try { @@ -261,7 +261,7 @@ export async function refresh< export async function loadMore< ED extends EntityDict & BaseEntityDict, T extends keyof ED, - Cxt extends Context>(this: ComponentFullThisType) { + Cxt extends Context>(this: ComponentFullThisType) { if (this.state.oakEntity && this.state.oakFullpath) { try { await this.features.runningTree.loadMore(this.state.oakFullpath); @@ -278,8 +278,8 @@ export async function execute< ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends Context>( - this: ComponentFullThisType, - operation?: Omit, + this: ComponentFullThisType, + data?: ED[T]['Update']['data'] | Record, path?: string) { if (this.state.oakExecuting) { throw new Error('请仔细设计按钮状态,不要允许重复点击!'); @@ -287,49 +287,20 @@ export async function execute< /* this.setState({ oakFocused: undefined, }); */ - try { - const fullpath = path - ? `${this.state.oakFullpath}.${path}` - : this.state.oakFullpath; - const result = await this.features.runningTree.execute(fullpath, operation); - await this.setMessage({ - type: 'success', - content: '操作成功', - }); - return result; - } catch (err) { - if (err instanceof OakUserException) { - if (err instanceof OakInputIllegalException) { - const attrs = err.getAttributes(); - const entity = err.getEntity(); - const message = err.message; - /* this.setState({ - oakFocused: { - attr: attrs[0], - message, - }, - oakExecuting: false, - }); */ - const attrNames = attrs.map(attr => this.t(`${entity}:attr.${attr}`)).filter(ele => !!ele); - this.setMessage({ - type: 'error', - content: attrNames.length > 0 ? `「${attrNames.join(',')}」${message}` : message, - }); - throw err; - } - } - this.setMessage({ - type: 'error', - content: (err as Error).message, - }); - throw err; - } + + const fullpath = path ? `${this.state.oakFullpath}.${path}` : this.state.oakFullpath; + const result = await this.features.runningTree.execute(fullpath, data); + await this.setMessage({ + type: 'success', + content: '操作成功', + }); + return result; } export function callPicker< ED extends EntityDict & BaseEntityDict, T extends keyof ED, - Cxt extends Context>(this: ComponentFullThisType, attr: string, params: Record = {}) { + Cxt extends Context>(this: ComponentFullThisType, attr: string, params: Record = {}) { if (this.state.oakExecuting) { return; } @@ -354,55 +325,12 @@ export function callPicker< }); } -export async function setUpdateData< - ED extends EntityDict & BaseEntityDict, - T extends keyof ED, - Cxt extends Context>(this: ComponentFullThisType, attr: string, data: any) { - assert(attr.indexOf('.') === -1, 'setUpdateData只能设置当前对象属性,子层对象请写完整的addOperation') - if (this.props.oakId) { - return this.addOperation({ - action: 'update', - data: { - [attr]: data, - } - } as ED[T]['Update']); - } - else { - await this.addOperation({ - action: 'create', - data: { - [attr]: data, - } - } as ED[T]['CreateSingle']); - } -} - -export async function setMultiAttrUpdateData< - ED extends EntityDict & BaseEntityDict, - T extends keyof ED, - Cxt extends Context>(this: ComponentFullThisType, data: Record) { - for (const key in data) { - assert(key.indexOf('.') === -1, 'setMultiAttrUpdateData只能设置当前对象属性,子层对象请写完整的addOperation'); - } - if (this.props.oakId) { - return this.addOperation({ - action: 'update', - data, - } as ED[T]['Update']); - } - else { - await this.addOperation({ - action: 'create', - data, - } as ED[T]['CreateSingle']); - } -} export function destroyNode< -ED extends EntityDict & BaseEntityDict, -T extends keyof ED, -Cxt extends Context>( - this: ComponentFullThisType) { + ED extends EntityDict & BaseEntityDict, + T extends keyof ED, + Cxt extends Context>( + this: ComponentFullThisType) { assert(this.state.oakFullpath); this.features.runningTree.destroyNode(this.state.oakFullpath); unset(this.state, ['oakFullpath', 'oakEntity']); diff --git a/src/page.web.tsx b/src/page.web.tsx index 51426e6c..80113f3a 100644 --- a/src/page.web.tsx +++ b/src/page.web.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { withRouter, PullToRefresh } from './platforms/web'; import { get } from 'oak-domain/lib/utils/lodash'; import { CommonAspectDict } from 'oak-common-aspect'; -import { Aspect, CheckerType, Context, DeduceSorterItem, EntityDict } from 'oak-domain/lib/types'; +import { Action, Aspect, CheckerType, Context, DeduceSorterItem, EntityDict } from 'oak-domain/lib/types'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; import { BasicFeatures } from './features'; import { NamedFilterItem, NamedSorterItem } from './types/NamedCondition'; @@ -14,11 +14,14 @@ import { ComponentProps, OakComponentOption, OakNavigateToParameters, + WebComponentCommonMethodNames, + WebComponentListMethodNames, + WebComponentSingleMethodNames, } from './types/Page'; import { subscribe, unsubscribe, onPathSet, reRender, refresh, - loadMore, execute, callPicker, setUpdateData, setMultiAttrUpdateData, + loadMore, execute, callPicker, destroyNode, } from './page.common'; import { MessageProps } from './types/Message'; @@ -38,7 +41,7 @@ abstract class OakComponentBase< TMethod extends WechatMiniprogram.Component.MethodOption > extends React.PureComponent, ComponentData> { abstract features: FD & BasicFeatures>; - abstract option: OakComponentOption; + abstract oakOption: OakComponentOption; subscribe() { subscribe.call(this as any); @@ -49,7 +52,7 @@ abstract class OakComponentBase< } onPathSet() { - return onPathSet.call(this as any, this.option as any); + return onPathSet.call(this as any, this.oakOption as any); } triggerEvent( @@ -123,10 +126,10 @@ abstract class OakComponentBase< } reRender(extra?: Record) { - return reRender.call(this as any, this.option as any, extra); + return reRender.call(this as any, this.oakOption as any, extra); } - navigateTo(options: { url: string } & OakNavigateToParameters, state?: Record, disableNamespace?: boolean) { + navigateTo(options: { url: string } & OakNavigateToParameters, state?: Record, disableNamespace?: boolean) { const { url, ...rest } = options; let url2 = url.includes('?') ? url.concat( @@ -158,7 +161,7 @@ abstract class OakComponentBase< return this.props.navigate(url2, { replace: false, state }); } - navigateBack(option: { delta?: number }) { + navigateBack(option?: { delta?: number }) { const { delta } = option || {}; return new Promise((resolve, reject) => { try { @@ -170,7 +173,7 @@ abstract class OakComponentBase< }); } - redirectTo(options: { url: string } & OakNavigateToParameters, state?: Record, disableNamespace?: boolean) { + redirectTo(options: { url: string } & OakNavigateToParameters, state?: Record, disableNamespace?: boolean) { const { url, ...rest } = options; let url2 = url.includes('?') ? url.concat( @@ -245,12 +248,35 @@ abstract class OakComponentBase< } } */ - addOperation(operation: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise, path?: string) { - const path2 = path ? `${this.state.oakFullpath}.${path}` : this.state.oakFullpath; - return this.features.runningTree.addOperation(path2, operation, beforeExecute, afterExecute); + addItem(data: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise) { + return this.features.runningTree.addItem(this.state.oakFullpath, data, beforeExecute, afterExecute); } - cleanOperation(path?: string) { + removeItem(id: string, beforeExecute?: () => Promise, afterExecute?: () => Promise) { + return this.features.runningTree.removeItem(this.state.oakFullpath, id, beforeExecute, afterExecute); + } + + updateItem(data: ED[T]['Update']['data'], id: string, action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise) { + return this.features.runningTree.updateItem(this.state.oakFullpath, data, id, action, beforeExecute, afterExecute); + } + + recoverItem(id: string) { + return this.features.runningTree.recoverItem(this.state.oakFullpath, id); + } + + create(data: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise) { + return this.features.runningTree.create(this.state.oakFullpath, data, beforeExecute, afterExecute); + } + + update(data: ED[T]['Update']['data'], action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise) { + return this.features.runningTree.update(this.state.oakFullpath, data, action, beforeExecute, afterExecute); + } + + remove(beforeExecute?: () => Promise, afterExecute?: () => Promise) { + return this.features.runningTree.remove(this.state.oakFullpath, beforeExecute, afterExecute); + } + + clean(path?: string) { const path2 = path ? `${this.state.oakFullpath}.${path}` : this.state.oakFullpath; return this.features.runningTree.clean(path2); } @@ -263,12 +289,12 @@ abstract class OakComponentBase< return callPicker.call(this as any, attr, params); } - execute(operation?: ED[T]['Operation']) { - return execute.call(this as any, operation); + execute(data?: ED[T]['Update']['data'] | Record) { + return execute.call(this as any, data); } getFreshValue(path?: string) { - const path2 = path? `${this.state.oakFullpath}.${path}` : this.state.oakFullpath; + const path2 = path ? `${this.state.oakFullpath}.${path}` : this.state.oakFullpath; return this.features.runningTree.getFreshValue(path2) as Promise; } @@ -290,14 +316,6 @@ abstract class OakComponentBase< return refresh.call(this as any); } - setUpdateData(attr: string, data: any) { - return setUpdateData.call(this as any, attr, data); - } - - setMultiAttrUpdateData(data: Record) { - return setMultiAttrUpdateData.call(this as any, data); - } - loadMore() { return loadMore.call(this as any); } @@ -534,7 +552,7 @@ export function createComponent< features: BasicFeatures> & FD, ) { const { - data, projection, properties, entity, methods, lifetimes, observers, Render, path + data, methods, lifetimes, observers, getRender, path } = option as OakComponentOption< ED, T, @@ -548,28 +566,121 @@ export function createComponent< TProperty, TMethod > & { - Render: React.ComponentType; + getRender: () => React.ComponentType; }; const { fn } = translateObservers(observers); - const props = {} as Record; class OakComponentWrapper extends OakComponentBase { features = features; - option = option; + oakOption = option; isReachBottom = false; methodProps: Record; constructor(props: ComponentProps) { super(props); - const methodProps: Record = { + const methodProps: Record = { t: (key: string, params?: object) => this.t(key, params), - execute: async (data: Record) => { - await this.setMultiAttrUpdateData(data); - await this.execute(); + execute: (data: Record) => { + return this.execute(data); }, - tryExecute: async (data: Record, path?: string) => this.tryExecute(path), - }; + refresh: () => { + return this.refresh(); + }, + setNotification: (data: NotificationProps) => { + return this.setNotification(data); + }, + setMessage: (data: MessageProps) => { + return this.setMessage(data); + }, + navigateTo: ( + options: { url: string } & OakNavigateToParameters, + state?: Record, + disableNamespace?: boolean + ) => { + return this.navigateTo(options, state, disableNamespace); + }, + navigateBack: (options?: { delta: number }) => { + return this.navigateBack(options); + }, + redirectTo: ( + options: Parameters[0] & + OakNavigateToParameters, + state?: Record, + disableNamespace?: boolean + ) => { + return this.redirectTo(options, state, disableNamespace); + }, + clean: (path?: string) => { + return this.clean(path); + } + }; + if (option.isList) { + Object.assign(methodProps, { + addItem: (data: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise) => { + return this.addItem(data, beforeExecute, afterExecute); + }, + removeItem: (id: string, beforeExecute?: () => Promise, afterExecute?: () => Promise) => { + return this.removeItem(id, beforeExecute, afterExecute); + }, + updateItem: (data: ED[T]['Update']['data'], id: string, action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise) => { + return this.updateItem(data, id, action, beforeExecute, afterExecute); + }, + setFilters: (filters: NamedFilterItem[]) => { + return this.setFilters(filters); + }, + addNamedFilter: (filter: NamedFilterItem, refresh?: boolean) => { + return this.addNamedFilter(filter, refresh); + }, + removeNamedFilter: ( + filter: NamedFilterItem, + refresh?: boolean + ) => { + return this.removeNamedFilter(filter, refresh); + }, + removeNamedFilterByName: (name: string, refresh?: boolean) => { + return this.removeNamedFilterByName(name, refresh); + }, + setNamedSorters: (sorters: NamedSorterItem[]) => { + return this.setNamedSorters(sorters); + }, + addNamedSorter: (sorter: NamedSorterItem, refresh?: boolean) => { + return this.addNamedSorter(sorter, refresh); + }, + removeNamedSorter: ( + sorter: NamedSorterItem, + refresh?: boolean + ) => { + return this.removeNamedSorter(sorter, refresh); + }, + removeNamedSorterByName: (name: string, refresh?: boolean) => { + return this.removeNamedSorterByName(name, refresh); + }, + setPageSize: (pageSize: number) => { + return this.setPageSize(pageSize); + }, + setCurrentPage: (current: number) => { + return this.setCurrentPage(current); + }, + loadMore: () => { + return this.loadMore(); + } + } as Record); + } + else { + Object.assign(methodProps, { + create: (data: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise) => { + return this.create(data, beforeExecute, afterExecute); + }, + update: (data: ED[T]['Update']['data'], action: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise) => { + return this.update(data, action, beforeExecute, afterExecute); + }, + remove: (beforeExecute?: () => Promise, afterExecute?: () => Promise) => { + return this.remove(beforeExecute, afterExecute); + }, + } as Record); + } + if (methods) { for (const m in methods) { Object.assign(this, { @@ -673,6 +784,7 @@ export function createComponent< render(): React.ReactNode { const { oakPullDownRefreshLoading } = this.state; const { oakDisablePulldownRefresh = false } = this.props; + const Render = getRender.call(this); if (this.supportPullDownRefresh() && !oakDisablePulldownRefresh) { const Child = React.cloneElement( @@ -694,11 +806,17 @@ export function createComponent< { getScrollContainer: () => document.body, }, - + ); return Child; } - return ; + return ; } }; return withRouter(OakComponentWrapper, option); diff --git a/src/types/Page.ts b/src/types/Page.ts index 630c9279..29e2cefe 100644 --- a/src/types/Page.ts +++ b/src/types/Page.ts @@ -116,13 +116,15 @@ export type ComponentPublicThisType< detail?: DetailType, options?: WechatMiniprogram.Component.TriggerEventOption ) => void; - } & TMethod & OakCommonComponentMethods & (IsList extends true ? OakListComponentMethods : {}); + } & TMethod & OakCommonComponentMethods & (IsList extends true ? OakListComponentMethods : OakSingleComponentMethods); export type ComponentFullThisType< ED extends EntityDict & BaseEntityDict, T extends keyof ED, + IsList extends boolean, Cxt extends Context > = { + subscribed?: () => void; features: BasicFeatures>; state: OakComponentData; props: ComponentProps; @@ -135,7 +137,7 @@ export type ComponentFullThisType< detail?: DetailType, options?: WechatMiniprogram.Component.TriggerEventOption ) => void; - } & OakCommonComponentMethods & OakListComponentMethods & OakHiddenComponentMethods; + } & OakCommonComponentMethods & (IsList extends true ? OakListComponentMethods : OakSingleComponentMethods); export type OakComponentOption< ED extends EntityDict & BaseEntityDict, @@ -247,23 +249,26 @@ export type OakCommonComponentMethods< disableNamespace?: boolean ) => Promise; // setProps: (props: Record, usingState?: true) => void; - addOperation: (operation: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise, path?: string) => Promise; - cleanOperation: (path?: string) => void; - + clean: (path?: string) => void; + t(key: string, params?: object): string; callPicker: (attr: string, params: Record) => void; - execute: (operation?: Omit, path?: string) => Promise; + execute: (data?: ED[T]['Update']['data'] | Record, path?: string) => Promise; checkOperation: (ntity: T, action: ED[T]['Action'], filter?: ED[T]['Update']['filter'], checkerTypes?: CheckerType[]) => Promise; tryExecute: (path?: string) => Promise; getOperations: (path?: string) => Promise; - refresh: (extra?: any) => Promise; - setUpdateData: (data: string, attr: any) => Promise; - setMultiAttrUpdateData: (data: Record) => Promise; - setId: (id: string) => Promise; - unsetId: () => void; - getId: () => string | undefined; + refresh: () => Promise; }; +export type OakSingleComponentMethods = { + setId: (id: string) => Promise; + unsetId: () => void; + getId: () => string | undefined; + create: (data: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise) => Promise; + update: (data: ED[T]['Update']['data'], action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise) => Promise; + remove: (beforeExecute?: () => Promise, afterExecute?: () => Promise) => Promise; +} + export type OakListComponentMethods = { loadMore: () => Promise; setFilters: (filters: NamedFilterItem[]) => Promise; @@ -291,6 +296,10 @@ export type OakListComponentMethods Pagination | undefined; setPageSize: (pageSize: number) => void; setCurrentPage: (current: number) => void; + + addItem: (data: Omit, beforeExecute?: () => Promise, afterExecute?: () => Promise) => Promise; + removeItem: (id: string, beforeExecute?: () => Promise, afterExecute?: () => Promise) => Promise; + updateItem: (data: ED[T]['Update']['data'], id: string, action?: ED[T]['Action'], beforeExecute?: () => Promise, afterExecute?: () => Promise) => Promise; }; type ComponentOnPropsChangeOption = { @@ -307,21 +316,21 @@ export type OakComponentOnlyMethods = { export type OakComponentData< ED extends EntityDict & BaseEntityDict, T extends keyof ED -> = { - oakExecuting: boolean; - oakAllowExecuting: boolean | OakUserException; - oakFocused: { - attr: string; - message: string; + > = { + oakExecuting: boolean; + oakAllowExecuting: boolean | OakUserException; + oakFocused: { + attr: string; + message: string; + }; + oakDirty: boolean; + oakLoading: boolean; + oakLoadingMore: boolean; + oakPullDownRefreshLoading: boolean; + oakEntity: T; + oakFullpath: string; + oakLegalActions?: ED[T]['Action'][]; }; - oakDirty: boolean; - oakLoading: boolean; - oakLoadingMore: boolean; - oakPullDownRefreshLoading: boolean; - oakEntity: T; - oakFullpath: string; - oakLegalActions?: ED[T]['Action'][]; -}; export type MakeOakComponent< ED extends EntityDict & BaseEntityDict, @@ -351,3 +360,24 @@ export type MakeOakComponent< TMethod > ) => React.ComponentType; + +// 暴露给组件的方法 +export type WebComponentCommonMethodNames = 'setNotification' | 'setMessage' | 'navigateTo' | 'navigateBack' | 'redirectTo' | 'clean' | 't' | 'execute' | 'refresh'; + +// 暴露给list组件的方法 +export type WebComponentListMethodNames = 'loadMore' | 'setFilters' | 'addNamedFilter' | 'removeNamedFilter' | 'removeNamedFilterByName' | 'setNamedSorters' + | 'addNamedSorter' | 'removeNamedSorter' | 'removeNamedSorterByName' | 'setPageSize' | 'setCurrentPage' | 'addItem' | 'removeItem' | 'updateItem'; + +// 暴露给single组件的方法 +export type WebComponentSingleMethodNames = 'create' | 'update' | 'remove'; + +export type WebComponentProps< + ED extends EntityDict & BaseEntityDict, + T extends keyof ED, + IsList extends boolean, + TData extends WechatMiniprogram.Component.DataOption = {}, + TMethod extends WechatMiniprogram.Component.MethodOption = {}> = { + methods: TMethod & Pick, WebComponentCommonMethodNames> + & (IsList extends true ? Pick, WebComponentListMethodNames> : Pick, WebComponentSingleMethodNames>); + data: TData & OakComponentData & (IsList extends true ? OakListComponentProperties : {}); + }