Merge branch 'dev' into release
This commit is contained in:
commit
1f06e12a00
|
|
@ -243,12 +243,13 @@ export declare class RunningTree<ED extends EntityDict & BaseEntityDict> extends
|
|||
private root;
|
||||
private nodeCountDict;
|
||||
private pageNodeStack;
|
||||
private singletonDict;
|
||||
constructor(cache: Cache<ED>, schema: StorageSchema<ED>);
|
||||
private increaseNodeCount;
|
||||
private decreaseNodeCount;
|
||||
private pushNodeIntoStack;
|
||||
private popNodeFromStack;
|
||||
createNode<T extends keyof ED>(options: CreateNodeOptions<ED, T>, isPage: boolean): ListNode<ED, T> | SingleNode<ED, T> | VirtualNode<ED>;
|
||||
createNode<T extends keyof ED>(options: CreateNodeOptions<ED, T>, isPage: boolean, singleton: boolean): ListNode<ED, T> | SingleNode<ED, T> | VirtualNode<ED>;
|
||||
private checkSingleNodeIsModiNode;
|
||||
checkIsModiNode(path: string): boolean;
|
||||
private findNode;
|
||||
|
|
|
|||
|
|
@ -1570,15 +1570,12 @@ class VirtualNode extends Feature {
|
|||
}
|
||||
}
|
||||
function analyzePath(path) {
|
||||
const idx = path.lastIndexOf('.');
|
||||
if (idx !== -1) {
|
||||
return {
|
||||
parent: path.slice(0, idx),
|
||||
path: path.slice(idx + 1),
|
||||
};
|
||||
}
|
||||
const paths = path.split('.');
|
||||
const length = paths.length;
|
||||
return {
|
||||
path,
|
||||
root: paths[0],
|
||||
parent: length > 1 ? paths.slice(0, length - 1).join('.') : '',
|
||||
path: paths[length - 1],
|
||||
};
|
||||
}
|
||||
export class RunningTree extends Feature {
|
||||
|
|
@ -1587,6 +1584,7 @@ export class RunningTree extends Feature {
|
|||
root;
|
||||
nodeCountDict;
|
||||
pageNodeStack;
|
||||
singletonDict;
|
||||
constructor(cache, schema) {
|
||||
super();
|
||||
// this.aspectWrapper = aspectWrapper;
|
||||
|
|
@ -1595,6 +1593,7 @@ export class RunningTree extends Feature {
|
|||
this.root = {};
|
||||
this.nodeCountDict = {};
|
||||
this.pageNodeStack = {};
|
||||
this.singletonDict = {};
|
||||
}
|
||||
increaseNodeCount(path) {
|
||||
if (!this.nodeCountDict[path]) {
|
||||
|
|
@ -1643,13 +1642,26 @@ export class RunningTree extends Feature {
|
|||
this.nodeCountDict[path] = count;
|
||||
}
|
||||
}
|
||||
createNode(options, isPage) {
|
||||
createNode(options, isPage, singleton) {
|
||||
const { entity, pagination, path: fullPath, filters, sorters, projection, isList, id, actions, cascadeActions, getTotal, } = options;
|
||||
let node;
|
||||
const { parent, path } = analyzePath(fullPath);
|
||||
const parentNode = parent ? this.findNode(parent) : undefined;
|
||||
node = this.findNode(fullPath);
|
||||
|
||||
|
||||
if (singleton) {
|
||||
assert(!parentNode, 'singleton页面必须是根结点');
|
||||
this.singletonDict[fullPath] = 1;
|
||||
}
|
||||
|
||||
if (node) {
|
||||
if (singleton) {
|
||||
assert(!parentNode, 'singleton页面必须是根结点');
|
||||
this.increaseNodeCount(fullPath);
|
||||
this.singletonDict[fullPath] = 1;
|
||||
return node;
|
||||
}
|
||||
if (isPage) {
|
||||
// 如果是page和过去的某个page在node path上一样,尝试把上一个node压栈保存状态,以使页面回到原来的page时保持其状态
|
||||
assert(!parentNode);
|
||||
|
|
@ -1777,7 +1789,8 @@ export class RunningTree extends Feature {
|
|||
}
|
||||
destroyNode(path, isPage) {
|
||||
const rest = this.decreaseNodeCount(path);
|
||||
if (rest === 0) {
|
||||
const { root } = analyzePath(path);
|
||||
if (rest === 0 && !this.singletonDict[root]) {
|
||||
const node = this.findNode(path);
|
||||
if (node) {
|
||||
const childPath = path.slice(path.lastIndexOf('.') + 1);
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
|||
export function onPathSet(option, isPage) {
|
||||
const { props, state } = this;
|
||||
const { oakPath, oakId, oakFilters } = props;
|
||||
const { entity, path, projection, isList, filters, sorters, pagination, getTotal } = option;
|
||||
const { singleton, entity, path, projection, isList, filters, sorters, pagination, getTotal } = option;
|
||||
const { features } = this;
|
||||
const oakPath2 = oakPath || path;
|
||||
assert(oakPath2);
|
||||
assert(!oakPath || !path);
|
||||
assert(oakPath || path);
|
||||
let oakPath2 = oakPath || path;
|
||||
if (oakId && isPage) {
|
||||
oakPath2 += `-${oakId}`;
|
||||
}
|
||||
if (entity) {
|
||||
// entity在node生命周期中不可可变,但sorter/filter/projection应当是运行时来决定
|
||||
const entity2 = entity instanceof Function ? entity.call(this) : entity;
|
||||
|
|
@ -103,7 +105,7 @@ export function onPathSet(option, isPage) {
|
|||
actions: typeof actions === 'function' ? () => actions.call(this) : actions,
|
||||
cascadeActions: cascadeActions && (() => cascadeActions.call(this)),
|
||||
getTotal: getTotal2,
|
||||
}, isPage);
|
||||
}, isPage, !!singleton);
|
||||
this.addFeatureSub('runningTree', (path2) => {
|
||||
// 父结点改变,子结点要重渲染
|
||||
if (this.state.oakFullpath?.includes(path2)) {
|
||||
|
|
@ -120,7 +122,7 @@ export function onPathSet(option, isPage) {
|
|||
// 创建virtualNode
|
||||
features.runningTree.createNode({
|
||||
path: oakPath2,
|
||||
}, isPage);
|
||||
}, isPage, !!singleton);
|
||||
this.addFeatureSub('runningTree', (path2) => {
|
||||
// 父结点改变,子结点要重渲染
|
||||
if (this.state.oakFullpath?.includes(path2)) {
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ const oakBehavior = Behavior({
|
|||
filter,
|
||||
}, checkerTypes);
|
||||
},
|
||||
tryExecute(path) {
|
||||
tryExecute(path, action) {
|
||||
const path2 = path
|
||||
? `${this.state.oakFullpath}.${path}`
|
||||
: this.state.oakFullpath;
|
||||
|
|
@ -214,7 +214,11 @@ const oakBehavior = Behavior({
|
|||
if (operations) {
|
||||
for (const oper of operations) {
|
||||
const { entity, operation } = oper;
|
||||
const result = this.checkOperation(entity, operation);
|
||||
const operation2 = action ? {
|
||||
...operation,
|
||||
action,
|
||||
} : operation;
|
||||
const result = this.checkOperation(entity, operation2);
|
||||
if (result !== true) {
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ export declare function createComponent<IsList extends boolean, ED extends Entit
|
|||
isDirty(path?: string | undefined): boolean;
|
||||
getFreshValue(path?: string | undefined): Partial<ED[keyof ED]["Schema"]> | Partial<ED[keyof ED]["Schema"]>[] | undefined;
|
||||
checkOperation<T2_2 extends keyof ED>(entity: T2_2, operation: Omit<ED[T2_2]["Operation"], "id">, checkerTypes?: (CheckerType | "relation")[] | undefined): boolean | import("oak-domain/lib/types").OakUserException<ED>;
|
||||
tryExecute(path?: string | undefined): boolean | import("oak-domain/lib/types").OakUserException<ED>;
|
||||
tryExecute(path?: string | undefined, action?: string | undefined): boolean | import("oak-domain/lib/types").OakUserException<ED>;
|
||||
getOperations<T_5 extends keyof ED>(path?: string | undefined): {
|
||||
entity: keyof ED;
|
||||
operation: ED[keyof ED]["Operation"];
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ class OakComponentBase extends React.PureComponent {
|
|||
}
|
||||
return this.features.cache.checkOperation(entity, operation, checkerTypes);
|
||||
}
|
||||
tryExecute(path) {
|
||||
tryExecute(path, action) {
|
||||
const path2 = path
|
||||
? `${this.state.oakFullpath}.${path}`
|
||||
: this.state.oakFullpath;
|
||||
|
|
@ -196,7 +196,11 @@ class OakComponentBase extends React.PureComponent {
|
|||
if (operations) {
|
||||
for (const oper of operations) {
|
||||
const { entity, operation } = oper;
|
||||
const result = this.checkOperation(entity, operation);
|
||||
const operation2 = action ? {
|
||||
...operation,
|
||||
action,
|
||||
} : operation;
|
||||
const result = this.checkOperation(entity, operation2);
|
||||
if (result !== true) {
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ type FeatureDef<IsList extends boolean, ED extends EntityDict & BaseEntityDict,
|
|||
type DevideWidth = 'pc' | 'mobile';
|
||||
interface ComponentOption<IsList extends boolean, ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>, FD extends Record<string, Feature>, FormedData extends Record<string, any>, TData extends DataOption, TProperty extends DataOption, TMethod extends Record<string, Function>, EMethod extends Record<string, Function> = {}> {
|
||||
isList?: IsList;
|
||||
singleton?: true;
|
||||
getTotal?: {
|
||||
max: number;
|
||||
deviceWidth?: DevideWidth | 'all';
|
||||
|
|
@ -168,7 +169,7 @@ export type OakCommonComponentMethods<ED extends EntityDict & BaseEntityDict, T
|
|||
data?: ED[T2]['Operation']['data'];
|
||||
filter?: ED[T2]['Operation']['filter'];
|
||||
}, checkerTypes?: (CheckerType | 'relation')[]) => boolean | OakUserException<ED>;
|
||||
tryExecute: (path?: string) => boolean | OakUserException<ED>;
|
||||
tryExecute: (path?: string, action?: ED[T]['Action']) => boolean | OakUserException<ED>;
|
||||
getOperations: (path?: string) => {
|
||||
operation: ED[T]['Operation'];
|
||||
entity: T;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ declare abstract class Node<ED extends EntityDict & BaseEntityDict, T extends ke
|
|||
protected loading: number;
|
||||
protected loadingMore: boolean;
|
||||
protected executing: boolean;
|
||||
private extraData;
|
||||
private actions?;
|
||||
private cascadeActions?;
|
||||
constructor(entity: T, schema: StorageSchema<ED>, cache: Cache<ED>, projection?: ED[T]['Selection']['data'] | (() => Promise<ED[T]['Selection']['data']>), parent?: SingleNode<ED, keyof ED> | ListNode<ED, keyof ED> | VirtualNode<ED>, path?: string, actions?: ActionDef<ED, T>[] | (() => ActionDef<ED, T>[]), cascadeActions?: () => {
|
||||
|
|
@ -36,6 +37,8 @@ declare abstract class Node<ED extends EntityDict & BaseEntityDict, T extends ke
|
|||
protected getProjection(): ED[T]['Selection']['data'] | undefined;
|
||||
setProjection(projection: ED[T]['Selection']['data']): void;
|
||||
protected judgeRelation(attr: string): string | 0 | 1 | string[] | 2 | -1;
|
||||
saveExtraData(key: string, data: any): void;
|
||||
loadExtraData(key: string): any;
|
||||
}
|
||||
declare class ListNode<ED extends EntityDict & BaseEntityDict, T extends keyof ED> extends Node<ED, T> {
|
||||
private updates;
|
||||
|
|
@ -194,6 +197,7 @@ declare class VirtualNode<ED extends EntityDict & BaseEntityDict> extends Featur
|
|||
private dirty;
|
||||
private executing;
|
||||
private loading;
|
||||
private extraData;
|
||||
protected children: Record<string, SingleNode<ED, keyof ED> | ListNode<ED, keyof ED> | VirtualNode<ED>>;
|
||||
constructor(path?: string, parent?: VirtualNode<ED>);
|
||||
getModiOperations(): Array<{
|
||||
|
|
@ -219,6 +223,8 @@ declare class VirtualNode<ED extends EntityDict & BaseEntityDict> extends Featur
|
|||
isLoading(): boolean;
|
||||
clean(dontPublish?: true): void;
|
||||
checkIfClean(): void;
|
||||
saveExtraData(key: string, data: any): void;
|
||||
loadExtraData(key: string): any;
|
||||
}
|
||||
export type CreateNodeOptions<ED extends EntityDict & BaseEntityDict, T extends keyof ED> = {
|
||||
path: string;
|
||||
|
|
@ -243,12 +249,13 @@ export declare class RunningTree<ED extends EntityDict & BaseEntityDict> extends
|
|||
private root;
|
||||
private nodeCountDict;
|
||||
private pageNodeStack;
|
||||
private singletonDict;
|
||||
constructor(cache: Cache<ED>, schema: StorageSchema<ED>);
|
||||
private increaseNodeCount;
|
||||
private decreaseNodeCount;
|
||||
private pushNodeIntoStack;
|
||||
private popNodeFromStack;
|
||||
createNode<T extends keyof ED>(options: CreateNodeOptions<ED, T>, isPage: boolean): ListNode<ED, T> | SingleNode<ED, T> | VirtualNode<ED>;
|
||||
createNode<T extends keyof ED>(options: CreateNodeOptions<ED, T>, isPage: boolean, singleton: boolean): ListNode<ED, T> | SingleNode<ED, T> | VirtualNode<ED>;
|
||||
private checkSingleNodeIsModiNode;
|
||||
checkIsModiNode(path: string): boolean;
|
||||
private findNode;
|
||||
|
|
@ -320,5 +327,7 @@ export declare class RunningTree<ED extends EntityDict & BaseEntityDict> extends
|
|||
}>;
|
||||
clean(path: string, dontPublish?: true): void;
|
||||
getRoot(): Record<string, SingleNode<ED, keyof ED> | ListNode<ED, keyof ED> | VirtualNode<ED>>;
|
||||
saveExtraData(path: string, key: string, data: any): void;
|
||||
loadExtraData(path: string, key: string): any;
|
||||
}
|
||||
export {};
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ class Node extends Feature_1.Feature {
|
|||
loading;
|
||||
loadingMore;
|
||||
executing;
|
||||
extraData = {};
|
||||
actions;
|
||||
cascadeActions;
|
||||
constructor(entity, schema, cache, projection, parent, path, actions, cascadeActions) {
|
||||
|
|
@ -86,6 +87,12 @@ class Node extends Feature_1.Feature {
|
|||
const attr2 = attr.split(':')[0]; // 处理attr:next
|
||||
return (0, relation_1.judgeRelation)(this.schema, this.entity, attr2);
|
||||
}
|
||||
saveExtraData(key, data) {
|
||||
this.extraData[key] = data;
|
||||
}
|
||||
loadExtraData(key) {
|
||||
return this.extraData[key];
|
||||
}
|
||||
}
|
||||
const DEFAULT_PAGINATION = {
|
||||
currentPage: 0,
|
||||
|
|
@ -1433,6 +1440,7 @@ class VirtualNode extends Feature_1.Feature {
|
|||
dirty;
|
||||
executing;
|
||||
loading = false;
|
||||
extraData = {};
|
||||
children;
|
||||
constructor(path, parent) {
|
||||
super();
|
||||
|
|
@ -1571,17 +1579,20 @@ class VirtualNode extends Feature_1.Feature {
|
|||
}
|
||||
this.dirty = false;
|
||||
}
|
||||
saveExtraData(key, data) {
|
||||
this.extraData[key] = data;
|
||||
}
|
||||
loadExtraData(key) {
|
||||
return this.extraData[key];
|
||||
}
|
||||
}
|
||||
function analyzePath(path) {
|
||||
const idx = path.lastIndexOf('.');
|
||||
if (idx !== -1) {
|
||||
return {
|
||||
parent: path.slice(0, idx),
|
||||
path: path.slice(idx + 1),
|
||||
};
|
||||
}
|
||||
const paths = path.split('.');
|
||||
const length = paths.length;
|
||||
return {
|
||||
path,
|
||||
root: paths[0],
|
||||
parent: length > 1 ? paths.slice(0, length - 1).join('.') : '',
|
||||
path: paths[length - 1],
|
||||
};
|
||||
}
|
||||
class RunningTree extends Feature_1.Feature {
|
||||
|
|
@ -1590,6 +1601,7 @@ class RunningTree extends Feature_1.Feature {
|
|||
root;
|
||||
nodeCountDict;
|
||||
pageNodeStack;
|
||||
singletonDict;
|
||||
constructor(cache, schema) {
|
||||
super();
|
||||
// this.aspectWrapper = aspectWrapper;
|
||||
|
|
@ -1598,6 +1610,7 @@ class RunningTree extends Feature_1.Feature {
|
|||
this.root = {};
|
||||
this.nodeCountDict = {};
|
||||
this.pageNodeStack = {};
|
||||
this.singletonDict = {};
|
||||
}
|
||||
increaseNodeCount(path) {
|
||||
if (!this.nodeCountDict[path]) {
|
||||
|
|
@ -1646,13 +1659,21 @@ class RunningTree extends Feature_1.Feature {
|
|||
this.nodeCountDict[path] = count;
|
||||
}
|
||||
}
|
||||
createNode(options, isPage) {
|
||||
createNode(options, isPage, singleton) {
|
||||
const { entity, pagination, path: fullPath, filters, sorters, projection, isList, id, actions, cascadeActions, getTotal, } = options;
|
||||
let node;
|
||||
const { parent, path } = analyzePath(fullPath);
|
||||
const parentNode = parent ? this.findNode(parent) : undefined;
|
||||
node = this.findNode(fullPath);
|
||||
if (singleton) {
|
||||
(0, assert_1.assert)(!parentNode, 'singleton页面必须是根结点');
|
||||
this.singletonDict[fullPath] = 1;
|
||||
}
|
||||
if (node) {
|
||||
if (singleton) {
|
||||
this.increaseNodeCount(fullPath);
|
||||
return node;
|
||||
}
|
||||
if (isPage) {
|
||||
// 如果是page和过去的某个page在node path上一样,尝试把上一个node压栈保存状态,以使页面回到原来的page时保持其状态
|
||||
(0, assert_1.assert)(!parentNode);
|
||||
|
|
@ -1780,7 +1801,8 @@ class RunningTree extends Feature_1.Feature {
|
|||
}
|
||||
destroyNode(path, isPage) {
|
||||
const rest = this.decreaseNodeCount(path);
|
||||
if (rest === 0) {
|
||||
const { root } = analyzePath(path);
|
||||
if (rest === 0 && !this.singletonDict[root]) {
|
||||
const node = this.findNode(path);
|
||||
if (node) {
|
||||
const childPath = path.slice(path.lastIndexOf('.') + 1);
|
||||
|
|
@ -2127,5 +2149,13 @@ class RunningTree extends Feature_1.Feature {
|
|||
getRoot() {
|
||||
return this.root;
|
||||
}
|
||||
saveExtraData(path, key, data) {
|
||||
const node = this.findNode(path);
|
||||
node.saveExtraData(key, data);
|
||||
}
|
||||
loadExtraData(path, key) {
|
||||
const node = this.findNode(path);
|
||||
return node.loadExtraData(key);
|
||||
}
|
||||
}
|
||||
exports.RunningTree = RunningTree;
|
||||
|
|
|
|||
|
|
@ -9,11 +9,13 @@ const uuid_1 = require("oak-domain/lib/utils/uuid");
|
|||
function onPathSet(option, isPage) {
|
||||
const { props, state } = this;
|
||||
const { oakPath, oakId, oakFilters } = props;
|
||||
const { entity, path, projection, isList, filters, sorters, pagination, getTotal } = option;
|
||||
const { singleton, entity, path, projection, isList, filters, sorters, pagination, getTotal } = option;
|
||||
const { features } = this;
|
||||
const oakPath2 = oakPath || path;
|
||||
(0, assert_1.assert)(oakPath2);
|
||||
(0, assert_1.assert)(!oakPath || !path);
|
||||
(0, assert_1.assert)(oakPath || path);
|
||||
let oakPath2 = oakPath || path;
|
||||
if (oakId && isPage) {
|
||||
oakPath2 += `-${oakId}`;
|
||||
}
|
||||
if (entity) {
|
||||
// entity在node生命周期中不可可变,但sorter/filter/projection应当是运行时来决定
|
||||
const entity2 = entity instanceof Function ? entity.call(this) : entity;
|
||||
|
|
@ -106,7 +108,7 @@ function onPathSet(option, isPage) {
|
|||
actions: typeof actions === 'function' ? () => actions.call(this) : actions,
|
||||
cascadeActions: cascadeActions && (() => cascadeActions.call(this)),
|
||||
getTotal: getTotal2,
|
||||
}, isPage);
|
||||
}, isPage, !!singleton);
|
||||
this.addFeatureSub('runningTree', (path2) => {
|
||||
// 父结点改变,子结点要重渲染
|
||||
if (this.state.oakFullpath?.includes(path2)) {
|
||||
|
|
@ -123,7 +125,7 @@ function onPathSet(option, isPage) {
|
|||
// 创建virtualNode
|
||||
features.runningTree.createNode({
|
||||
path: oakPath2,
|
||||
}, isPage);
|
||||
}, isPage, !!singleton);
|
||||
this.addFeatureSub('runningTree', (path2) => {
|
||||
// 父结点改变,子结点要重渲染
|
||||
if (this.state.oakFullpath?.includes(path2)) {
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ const oakBehavior = Behavior({
|
|||
filter,
|
||||
}, checkerTypes);
|
||||
},
|
||||
tryExecute(path) {
|
||||
tryExecute(path, action) {
|
||||
const path2 = path
|
||||
? `${this.state.oakFullpath}.${path}`
|
||||
: this.state.oakFullpath;
|
||||
|
|
@ -217,7 +217,11 @@ const oakBehavior = Behavior({
|
|||
if (operations) {
|
||||
for (const oper of operations) {
|
||||
const { entity, operation } = oper;
|
||||
const result = this.checkOperation(entity, operation);
|
||||
const operation2 = action ? {
|
||||
...operation,
|
||||
action,
|
||||
} : operation;
|
||||
const result = this.checkOperation(entity, operation2);
|
||||
if (result !== true) {
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ export declare function createComponent<IsList extends boolean, ED extends Entit
|
|||
isDirty(path?: string | undefined): boolean;
|
||||
getFreshValue(path?: string | undefined): Partial<ED[keyof ED]["Schema"]> | Partial<ED[keyof ED]["Schema"]>[] | undefined;
|
||||
checkOperation<T2_2 extends keyof ED>(entity: T2_2, operation: Omit<ED[T2_2]["Operation"], "id">, checkerTypes?: (CheckerType | "relation")[] | undefined): boolean | import("oak-domain/lib/types").OakUserException<ED>;
|
||||
tryExecute(path?: string | undefined): boolean | import("oak-domain/lib/types").OakUserException<ED>;
|
||||
tryExecute(path?: string | undefined, action?: string | undefined): boolean | import("oak-domain/lib/types").OakUserException<ED>;
|
||||
getOperations<T_5 extends keyof ED>(path?: string | undefined): {
|
||||
entity: keyof ED;
|
||||
operation: ED[keyof ED]["Operation"];
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ class OakComponentBase extends react_1.default.PureComponent {
|
|||
}
|
||||
return this.features.cache.checkOperation(entity, operation, checkerTypes);
|
||||
}
|
||||
tryExecute(path) {
|
||||
tryExecute(path, action) {
|
||||
const path2 = path
|
||||
? `${this.state.oakFullpath}.${path}`
|
||||
: this.state.oakFullpath;
|
||||
|
|
@ -201,7 +201,11 @@ class OakComponentBase extends react_1.default.PureComponent {
|
|||
if (operations) {
|
||||
for (const oper of operations) {
|
||||
const { entity, operation } = oper;
|
||||
const result = this.checkOperation(entity, operation);
|
||||
const operation2 = action ? {
|
||||
...operation,
|
||||
action,
|
||||
} : operation;
|
||||
const result = this.checkOperation(entity, operation2);
|
||||
if (result !== true) {
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ type FeatureDef<IsList extends boolean, ED extends EntityDict & BaseEntityDict,
|
|||
type DevideWidth = 'pc' | 'mobile';
|
||||
interface ComponentOption<IsList extends boolean, ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>, FrontCxt extends SyncContext<ED>, AD extends Record<string, Aspect<ED, Cxt>>, FD extends Record<string, Feature>, FormedData extends Record<string, any>, TData extends DataOption, TProperty extends DataOption, TMethod extends Record<string, Function>, EMethod extends Record<string, Function> = {}> {
|
||||
isList?: IsList;
|
||||
singleton?: true;
|
||||
getTotal?: {
|
||||
max: number;
|
||||
deviceWidth?: DevideWidth | 'all';
|
||||
|
|
@ -168,7 +169,7 @@ export type OakCommonComponentMethods<ED extends EntityDict & BaseEntityDict, T
|
|||
data?: ED[T2]['Operation']['data'];
|
||||
filter?: ED[T2]['Operation']['filter'];
|
||||
}, checkerTypes?: (CheckerType | 'relation')[]) => boolean | OakUserException<ED>;
|
||||
tryExecute: (path?: string) => boolean | OakUserException<ED>;
|
||||
tryExecute: (path?: string, action?: ED[T]['Action']) => boolean | OakUserException<ED>;
|
||||
getOperations: (path?: string) => {
|
||||
operation: ED[T]['Operation'];
|
||||
entity: T;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "oak-frontend-base",
|
||||
"version": "5.2.3",
|
||||
"version": "5.2.4",
|
||||
"description": "oak框架中前端与业务逻辑无关的平台部分",
|
||||
"author": {
|
||||
"name": "XuChang"
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
"node-schedule": "^2.1.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"oak-common-aspect": "~3.0.0",
|
||||
"oak-domain": "~5.0.11",
|
||||
"oak-domain": "~5.0.12",
|
||||
"oak-memory-tree-store": "~3.3.1",
|
||||
"ol": "^7.3.0",
|
||||
"react-activation": "^0.12.4",
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ abstract class Node<
|
|||
protected loading: number;
|
||||
protected loadingMore: boolean;
|
||||
protected executing: boolean;
|
||||
private extraData: Record<string, any> = {};
|
||||
private actions?: ActionDef<ED, T>[] | (() => ActionDef<ED, T>[]);
|
||||
private cascadeActions?: () => {
|
||||
[K in keyof ED[T]['Schema']]?: ActionDef<ED, keyof ED>[];
|
||||
|
|
@ -122,6 +123,14 @@ abstract class Node<
|
|||
const attr2 = attr.split(':')[0]; // 处理attr:next
|
||||
return judgeRelation(this.schema, this.entity, attr2);
|
||||
}
|
||||
|
||||
saveExtraData(key: string, data: any) {
|
||||
this.extraData[key] = data;
|
||||
}
|
||||
|
||||
loadExtraData(key: string) {
|
||||
return this.extraData[key];
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_PAGINATION: Pagination = {
|
||||
|
|
@ -1686,11 +1695,11 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
|
|||
}
|
||||
}
|
||||
|
||||
class VirtualNode<
|
||||
ED extends EntityDict & BaseEntityDict> extends Feature {
|
||||
class VirtualNode<ED extends EntityDict & BaseEntityDict> extends Feature {
|
||||
private dirty: boolean;
|
||||
private executing: boolean;
|
||||
private loading = false;
|
||||
private extraData: Record<string, any> = {};
|
||||
protected children: Record<string, SingleNode<ED, keyof ED> | ListNode<ED, keyof ED> | VirtualNode<ED>>;
|
||||
constructor(path?: string, parent?: VirtualNode<ED>) {
|
||||
super();
|
||||
|
|
@ -1848,6 +1857,13 @@ class VirtualNode<
|
|||
this.dirty = false;
|
||||
}
|
||||
|
||||
saveExtraData(key: string, data: any) {
|
||||
this.extraData[key] = data;
|
||||
}
|
||||
|
||||
loadExtraData(key: string) {
|
||||
return this.extraData[key];
|
||||
}
|
||||
}
|
||||
|
||||
export type CreateNodeOptions<ED extends EntityDict & BaseEntityDict, T extends keyof ED> = {
|
||||
|
|
@ -1870,15 +1886,12 @@ export type CreateNodeOptions<ED extends EntityDict & BaseEntityDict, T extends
|
|||
|
||||
|
||||
function analyzePath(path: string) {
|
||||
const idx = path.lastIndexOf('.');
|
||||
if (idx !== -1) {
|
||||
return {
|
||||
parent: path.slice(0, idx),
|
||||
path: path.slice(idx + 1),
|
||||
};
|
||||
}
|
||||
const paths = path.split('.');
|
||||
const length = paths.length;
|
||||
return {
|
||||
path,
|
||||
root: paths[0],
|
||||
parent: length > 1 ? paths.slice(0, length - 1).join('.') : '',
|
||||
path: paths[length - 1],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1897,6 +1910,7 @@ export class RunningTree<ED extends EntityDict & BaseEntityDict> extends Feature
|
|||
count: number;
|
||||
node: Node<ED, keyof ED> | VirtualNode<ED>;
|
||||
}>>;
|
||||
private singletonDict: Record<string, 1>;
|
||||
|
||||
constructor(
|
||||
cache: Cache<ED>,
|
||||
|
|
@ -1909,6 +1923,7 @@ export class RunningTree<ED extends EntityDict & BaseEntityDict> extends Feature
|
|||
this.root = {};
|
||||
this.nodeCountDict = {};
|
||||
this.pageNodeStack = {};
|
||||
this.singletonDict = {};
|
||||
}
|
||||
|
||||
private increaseNodeCount(path: string) {
|
||||
|
|
@ -1963,9 +1978,7 @@ export class RunningTree<ED extends EntityDict & BaseEntityDict> extends Feature
|
|||
}
|
||||
}
|
||||
|
||||
createNode<T extends keyof ED>(options: CreateNodeOptions<ED, T>, isPage: boolean): ListNode<ED, T>
|
||||
| SingleNode<ED, T>
|
||||
| VirtualNode<ED> {
|
||||
createNode<T extends keyof ED>(options: CreateNodeOptions<ED, T>, isPage: boolean, singleton: boolean): ListNode<ED, T> | SingleNode<ED, T> | VirtualNode<ED> {
|
||||
const {
|
||||
entity,
|
||||
pagination,
|
||||
|
|
@ -1986,7 +1999,17 @@ export class RunningTree<ED extends EntityDict & BaseEntityDict> extends Feature
|
|||
| SingleNode<ED, T>
|
||||
| VirtualNode<ED>
|
||||
| undefined);
|
||||
|
||||
if (singleton) {
|
||||
assert(!parentNode, 'singleton页面必须是根结点');
|
||||
this.singletonDict[fullPath] = 1;
|
||||
}
|
||||
|
||||
if (node) {
|
||||
if (singleton) {
|
||||
this.increaseNodeCount(fullPath);
|
||||
return node;
|
||||
}
|
||||
if (isPage) {
|
||||
// 如果是page和过去的某个page在node path上一样,尝试把上一个node压栈保存状态,以使页面回到原来的page时保持其状态
|
||||
assert(!parentNode);
|
||||
|
|
@ -2142,7 +2165,8 @@ export class RunningTree<ED extends EntityDict & BaseEntityDict> extends Feature
|
|||
|
||||
destroyNode(path: string, isPage: boolean) {
|
||||
const rest = this.decreaseNodeCount(path);
|
||||
if (rest === 0) {
|
||||
const { root } = analyzePath(path);
|
||||
if (rest === 0 && !this.singletonDict[root]) {
|
||||
const node = this.findNode(path);
|
||||
if (node) {
|
||||
const childPath = path.slice(path.lastIndexOf('.') + 1);
|
||||
|
|
@ -2601,4 +2625,14 @@ export class RunningTree<ED extends EntityDict & BaseEntityDict> extends Feature
|
|||
getRoot() {
|
||||
return this.root;
|
||||
}
|
||||
|
||||
saveExtraData(path: string, key: string, data: any) {
|
||||
const node = this.findNode(path)!;
|
||||
node.saveExtraData(key, data);
|
||||
}
|
||||
|
||||
loadExtraData(path: string, key: string) {
|
||||
const node = this.findNode(path)!;
|
||||
return node.loadExtraData(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -35,12 +35,14 @@ export function onPathSet<
|
|||
isPage: boolean): Partial<OakComponentData<ED, T>> {
|
||||
const { props, state } = this;
|
||||
const { oakPath, oakId, oakFilters } = props as ComponentProps<ED, T, {}>;
|
||||
const { entity, path, projection, isList, filters, sorters, pagination, getTotal } = option;
|
||||
const { singleton, entity, path, projection, isList, filters, sorters, pagination, getTotal } = option;
|
||||
const { features } = this;
|
||||
|
||||
const oakPath2 = oakPath || path;
|
||||
assert(oakPath2);
|
||||
assert(!oakPath || !path);
|
||||
assert(oakPath || path);
|
||||
let oakPath2 = oakPath || path;
|
||||
if (oakId && isPage) {
|
||||
oakPath2 += `-${oakId}`;
|
||||
}
|
||||
if (entity) {
|
||||
// entity在node生命周期中不可可变,但sorter/filter/projection应当是运行时来决定
|
||||
const entity2 = entity instanceof Function ? entity.call(this) : entity;
|
||||
|
|
@ -145,7 +147,7 @@ export function onPathSet<
|
|||
actions: typeof actions === 'function' ? () => actions.call(this) : actions,
|
||||
cascadeActions: cascadeActions && (() => cascadeActions.call(this)),
|
||||
getTotal: getTotal2,
|
||||
}, isPage);
|
||||
}, isPage, !!singleton);
|
||||
this.addFeatureSub('runningTree', (path2: string) => {
|
||||
// 父结点改变,子结点要重渲染
|
||||
if (this.state.oakFullpath?.includes(path2)) {
|
||||
|
|
@ -163,7 +165,7 @@ export function onPathSet<
|
|||
// 创建virtualNode
|
||||
features.runningTree.createNode({
|
||||
path: oakPath2 as string,
|
||||
}, isPage);
|
||||
}, isPage, !!singleton);
|
||||
this.addFeatureSub('runningTree', (path2: string) => {
|
||||
// 父结点改变,子结点要重渲染
|
||||
if (this.state.oakFullpath?.includes(path2)) {
|
||||
|
|
@ -681,7 +683,7 @@ export function destroyNode<
|
|||
ED extends EntityDict & BaseEntityDict,
|
||||
T extends keyof ED
|
||||
>(this: ComponentFullThisType<ED, T>, isPage: boolean) {
|
||||
assert(this.state.oakFullpath);
|
||||
assert(this.state.oakFullpath);
|
||||
this.features.runningTree.destroyNode(this.state.oakFullpath, isPage);
|
||||
unset(this.state, ['oakFullpath', 'oakEntity']);
|
||||
}
|
||||
|
|
@ -342,7 +342,7 @@ const oakBehavior = Behavior<
|
|||
);
|
||||
},
|
||||
|
||||
tryExecute(path?: string) {
|
||||
tryExecute(path?: string, action?: string) {
|
||||
const path2 = path
|
||||
? `${this.state.oakFullpath}.${path}`
|
||||
: this.state.oakFullpath;
|
||||
|
|
@ -350,7 +350,11 @@ const oakBehavior = Behavior<
|
|||
if (operations) {
|
||||
for (const oper of operations) {
|
||||
const { entity, operation } = oper;
|
||||
const result = this.checkOperation(entity, operation);
|
||||
const operation2 = action ? {
|
||||
...operation,
|
||||
action,
|
||||
} : operation;
|
||||
const result = this.checkOperation(entity, operation2);
|
||||
if (result !== true) {
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,10 +40,10 @@ abstract class OakComponentBase<
|
|||
TData extends DataOption,
|
||||
TProperty extends DataOption,
|
||||
TMethod extends MethodOption
|
||||
> extends React.PureComponent<
|
||||
> extends React.PureComponent<
|
||||
ComponentProps<ED, T, TProperty>,
|
||||
ComponentData<ED, T, FormedData, TData>
|
||||
> {
|
||||
> {
|
||||
abstract features: FD &
|
||||
BasicFeatures<ED>;
|
||||
abstract oakOption: OakComponentOption<
|
||||
|
|
@ -362,7 +362,7 @@ abstract class OakComponentBase<
|
|||
);
|
||||
}
|
||||
|
||||
tryExecute(path?: string) {
|
||||
tryExecute(path?: string, action?: string) {
|
||||
const path2 = path
|
||||
? `${this.state.oakFullpath}.${path}`
|
||||
: this.state.oakFullpath;
|
||||
|
|
@ -370,7 +370,11 @@ abstract class OakComponentBase<
|
|||
if (operations) {
|
||||
for (const oper of operations) {
|
||||
const { entity, operation } = oper;
|
||||
const result = this.checkOperation(entity, operation);
|
||||
const operation2 = action ? {
|
||||
...operation,
|
||||
action,
|
||||
} : operation;
|
||||
const result = this.checkOperation(entity, operation2);
|
||||
if (result !== true) {
|
||||
return result;
|
||||
}
|
||||
|
|
@ -986,7 +990,10 @@ export function createComponent<
|
|||
|
||||
componentWillUnmount() {
|
||||
this.unsubscribeAll();
|
||||
this.state.oakFullpath && (this.iAmThePage() || !this.props.oakZombie) && destroyNode.call(this as any, this.iAmThePage());
|
||||
this.state.oakFullpath && (this.iAmThePage() || !this.props.oakZombie) && destroyNode.call(
|
||||
this as any,
|
||||
this.iAmThePage()
|
||||
);
|
||||
lifetimes?.detached && lifetimes.detached.call(this);
|
||||
this.unmounted = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ interface ComponentOption<
|
|||
EMethod extends Record<string, Function> = {},
|
||||
> {
|
||||
isList?: IsList;
|
||||
singleton?: true;
|
||||
getTotal?: {
|
||||
max: number;
|
||||
deviceWidth?: DevideWidth | 'all';
|
||||
|
|
@ -391,7 +392,7 @@ export type OakCommonComponentMethods<
|
|||
},
|
||||
checkerTypes?: (CheckerType | 'relation')[]
|
||||
) => boolean | OakUserException<ED>;
|
||||
tryExecute: (path?: string) => boolean | OakUserException<ED>;
|
||||
tryExecute: (path?: string, action?: ED[T]['Action']) => boolean | OakUserException<ED>;
|
||||
getOperations: (
|
||||
path?: string
|
||||
) => { operation: ED[T]['Operation']; entity: T }[] | undefined;
|
||||
|
|
|
|||
Loading…
Reference in New Issue