将oakFocused加回,修改了upsert的策略

This commit is contained in:
Xu Chang 2022-10-19 20:02:06 +08:00
parent 0d38589df6
commit cb2cf5853c
7 changed files with 211 additions and 157 deletions

View File

@ -150,7 +150,7 @@ export declare class RunningTree<ED extends EntityDict & BaseEntityDict, Cxt ext
addNamedSorter<T extends keyof ED>(path: string, sorter: NamedSorterItem<ED, T>, refresh?: boolean): Promise<void>;
removeNamedSorter<T extends keyof ED>(path: string, sorter: NamedSorterItem<ED, T>, refresh?: boolean): Promise<void>;
removeNamedSorterByName<T extends keyof ED>(path: string, name: string, refresh?: boolean): Promise<void>;
tryExecute(path: string): Promise<void>;
tryExecute(path: string): Promise<boolean>;
execute(path: string): Promise<void>;
clean(path: string): void;
getRoot(): Record<string, SingleNode<ED, keyof ED, Cxt, AD> | ListNode<ED, keyof ED, Cxt, AD>>;

View File

@ -1101,19 +1101,19 @@ var SingleNode = /** @class */ (function (_super) {
} */
SingleNode.prototype.getFreshValue = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var projection, _a, modiIds, operations, result;
var projection, _a, modiIds, operations, _b, operation, id, result;
var _this = this;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
if (!(typeof this.projection === 'function')) return [3 /*break*/, 2];
return [4 /*yield*/, this.projection()];
case 1:
_a = _b.sent();
_a = _c.sent();
return [3 /*break*/, 3];
case 2:
_a = this.projection;
_b.label = 3;
_c.label = 3;
case 3:
projection = _a;
modiIds = this.parent ? this.parent.getModiIds(this) : [];
@ -1127,6 +1127,11 @@ var SingleNode = /** @class */ (function (_super) {
},
}
}); });
_b = tslib_1.__read(this.operations, 1), operation = _b[0];
id = this.id;
if ((operation === null || operation === void 0 ? void 0 : operation.oper.action) === 'create') {
id = operation.oper.data.id;
}
operations.push.apply(operations, tslib_1.__spreadArray([], tslib_1.__read(this.operations.map(function (ele) { return ({
entity: _this.entity,
operation: ele.oper,
@ -1134,11 +1139,11 @@ var SingleNode = /** @class */ (function (_super) {
return [4 /*yield*/, this.cache.tryRedoOperationsThenSelect(this.entity, {
data: projection,
filter: {
id: this.id,
id: id,
},
}, operations)];
case 4:
result = (_b.sent()).result;
result = (_c.sent()).result;
return [2 /*return*/, result[0]];
}
});
@ -1146,10 +1151,10 @@ var SingleNode = /** @class */ (function (_super) {
};
SingleNode.prototype.addOperation = function (oper, beforeExecute, afterExecute) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var operation, merged, _a, _b, _c;
var _d;
return tslib_1.__generator(this, function (_e) {
switch (_e.label) {
var operation, _a, _b, _c, _d, _e, _f, _g, current;
var _h, _j;
return tslib_1.__generator(this, function (_k) {
switch (_k.label) {
case 0:
if (!oper.filter) {
Object.assign(oper, {
@ -1166,18 +1171,31 @@ var SingleNode = /** @class */ (function (_super) {
beforeExecute: beforeExecute,
afterExecute: afterExecute,
};
merged = mergeOperation(this.entity, this.schema, operation, this.operations);
if (!!merged) return [3 /*break*/, 2];
(0, assert_1.assert)(this.operations.length === 0); // singleNode上的merge应该不可能失败所有的操作都是基于id的
if (!(this.operations.length === 0)) return [3 /*break*/, 4];
if (!(oper.action === 'create')) return [3 /*break*/, 2];
_b = (_a = Object).assign;
_c = [oper];
_d = {};
_c = [oper.data];
_h = {};
return [4 /*yield*/, generateNewId()];
case 1:
_b.apply(_a, _c.concat([(_d.id = _e.sent(), _d)]));
this.operations.push(operation);
_e.label = 2;
_b.apply(_a, _c.concat([(_h.id = _k.sent(),
_h)]));
_k.label = 2;
case 2:
_e = (_d = Object).assign;
_f = [oper];
_j = {};
return [4 /*yield*/, generateNewId()];
case 3:
_e.apply(_d, _f.concat([(_j.id = _k.sent(), _j)]));
this.operations.push(operation);
return [3 /*break*/, 5];
case 4:
_g = tslib_1.__read(this.operations, 1), current = _g[0];
Object.assign(current.oper.data, oper.data);
mergeOperationTrigger(operation, current);
_k.label = 5;
case 5:
this.setDirty();
return [2 /*return*/];
}
@ -1794,10 +1812,8 @@ var RunningTree = /** @class */ (function (_super) {
operations = _a.sent();
if (!operations) return [3 /*break*/, 3];
return [4 /*yield*/, this.cache.tryRedoOperations(node.getEntity(), operations.map(function (ele) { return ele.oper; }))];
case 2:
_a.sent();
_a.label = 3;
case 3: return [2 /*return*/];
case 2: return [2 /*return*/, _a.sent()];
case 3: return [2 /*return*/, false];
}
});
});

View File

@ -152,14 +152,14 @@ function onPathSet(option) {
exports.onPathSet = onPathSet;
function reRender(option, extra) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var features, formData, rows, oakDirty, oakLoading, oakLoadingMore, oakExecuting, data, _a, k, data, _b;
var features, formData, rows, oakDirty, oakLoading, oakLoadingMore, oakExecuting, data, _a, k, oakAllowExecuting, err_1, data, _b;
var _c;
return tslib_1.__generator(this, function (_d) {
switch (_d.label) {
case 0:
features = this.features;
formData = option.formData;
if (!(this.state.oakEntity && this.state.oakFullpath)) return [3 /*break*/, 5];
if (!(this.state.oakEntity && this.state.oakFullpath)) return [3 /*break*/, 9];
return [4 /*yield*/, this.features.runningTree.getFreshValue(this.state.oakFullpath)];
case 1:
rows = _d.sent();
@ -192,61 +192,56 @@ function reRender(option, extra) {
if (extra) {
Object.assign(data, extra);
}
this.setState(data);
return [3 /*break*/, 9];
oakAllowExecuting = false;
_d.label = 5;
case 5:
if (!formData) return [3 /*break*/, 7];
_d.trys.push([5, 7, , 8]);
return [4 /*yield*/, this.features.runningTree.tryExecute(this.state.oakFullpath)];
case 6:
oakAllowExecuting = _d.sent();
return [3 /*break*/, 8];
case 7:
err_1 = _d.sent();
if (err_1 instanceof types_1.OakUserException) {
oakAllowExecuting = err_1;
}
else {
oakAllowExecuting = false;
throw err_1;
}
return [3 /*break*/, 8];
case 8:
Object.assign(data, {
oakAllowExecuting: oakAllowExecuting,
});
this.setState(data);
return [3 /*break*/, 13];
case 9:
if (!formData) return [3 /*break*/, 11];
return [4 /*yield*/, formData.call(this, {
features: features,
props: this.props,
})];
case 6:
case 10:
_b = _d.sent();
return [3 /*break*/, 8];
case 7:
return [3 /*break*/, 12];
case 11:
_b = {};
_d.label = 8;
case 8:
_d.label = 12;
case 12:
data = _b;
if (extra) {
Object.assign(data, extra);
}
this.setState(data);
_d.label = 9;
case 9: return [2 /*return*/];
_d.label = 13;
case 13: return [2 /*return*/];
}
});
});
}
exports.reRender = reRender;
function refresh() {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var err_1;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!(this.state.oakEntity && this.state.oakFullpath)) return [3 /*break*/, 4];
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.features.runningTree.refresh(this.state.oakFullpath)];
case 2:
_a.sent();
return [3 /*break*/, 4];
case 3:
err_1 = _a.sent();
this.setMessage({
type: 'error',
content: err_1.message,
});
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
});
}
exports.refresh = refresh;
function loadMore() {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var err_2;
return tslib_1.__generator(this, function (_a) {
@ -256,7 +251,7 @@ function loadMore() {
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.features.runningTree.loadMore(this.state.oakFullpath)];
return [4 /*yield*/, this.features.runningTree.refresh(this.state.oakFullpath)];
case 2:
_a.sent();
return [3 /*break*/, 4];
@ -272,67 +267,85 @@ function loadMore() {
});
});
}
exports.refresh = refresh;
function loadMore() {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var err_3;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!(this.state.oakEntity && this.state.oakFullpath)) return [3 /*break*/, 4];
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.features.runningTree.loadMore(this.state.oakFullpath)];
case 2:
_a.sent();
return [3 /*break*/, 4];
case 3:
err_3 = _a.sent();
this.setMessage({
type: 'error',
content: err_3.message,
});
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
});
}
exports.loadMore = loadMore;
function execute(legalExceptions, path) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var fullpath, result, err_3, attr, name_3;
var _a;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
var fullpath, result, err_4, attrs, message;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (this.state.oakExecuting) {
return [2 /*return*/];
}
this.setState({
oakFocused: {},
oakFocused: undefined,
oakExecuting: true,
});
_b.label = 1;
_a.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
_a.trys.push([1, 3, , 4]);
fullpath = path
? "".concat(this.state.oakFullpath, ".").concat(path)
: this.state.oakFullpath;
return [4 /*yield*/, this.features.runningTree.execute(fullpath)];
case 2:
result = _b.sent();
result = _a.sent();
this.setState({
oakExecuting: false,
});
this.setMessage({
type: 'success',
content: '操作成功',
});
return [2 /*return*/, result];
case 3:
err_3 = _b.sent();
if (err_3 instanceof types_1.OakException) {
if (err_3 instanceof types_1.OakInputIllegalException) {
attr = err_3.getAttributes()[0];
err_4 = _a.sent();
if (err_4 instanceof types_1.OakUserException) {
if (err_4 instanceof types_1.OakInputIllegalException) {
attrs = err_4.getAttributes();
message = err_4.message;
this.setState({
oakFocused: (_a = {},
_a[attr] = true,
_a),
oakFocused: {
attr: attrs[0],
message: message,
},
oakExecuting: false,
});
this.setMessage({
type: 'warning',
content: err_3.message,
});
}
else {
name_3 = err_3.constructor.name;
if (legalExceptions && legalExceptions.includes(name_3)) {
// 如果调用时就知道有异常,直接抛出
this.setState({
oakExecuting: false,
});
throw err_3;
}
return [2 /*return*/];
}
}
else {
this.setMessage({
type: 'warning',
content: err_3.message,
});
}
throw err_3;
this.setMessage({
type: 'error',
content: err_4.message,
});
throw err_4;
case 4: return [2 /*return*/];
}
});
@ -364,7 +377,6 @@ function callPicker(attr, params) {
exports.callPicker = callPicker;
function setUpdateData(attr, data) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var id;
var _a, _b;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
@ -377,22 +389,16 @@ function setUpdateData(attr, data) {
_a[attr] = data,
_a)
})];
case 1: return [4 /*yield*/, generateNewId()];
case 1: return [4 /*yield*/, this.addOperation({
action: 'create',
data: (_b = {},
_b[attr] = data,
_b)
})];
case 2:
id = _c.sent();
return [4 /*yield*/, this.addOperation({
action: 'create',
data: (_b = {
id: id
},
_b[attr] = data,
_b)
})];
case 3:
_c.sent();
this.setProps({ oakId: id });
_c.label = 4;
case 4: return [2 /*return*/];
_c.label = 3;
case 3: return [2 /*return*/];
}
});
});

8
lib/types/Page.d.ts vendored
View File

@ -1,6 +1,6 @@
/// <reference types="wechat-miniprogram" />
/// <reference types="react" />
import { Aspect, Context, EntityDict, DeduceSorterItem, SelectRowShape, CheckerType } from "oak-domain/lib/types";
import { Aspect, Context, EntityDict, DeduceSorterItem, SelectRowShape, CheckerType, OakUserException } from "oak-domain/lib/types";
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { CommonAspectDict } from 'oak-common-aspect';
import { Feature } from './Feature';
@ -183,7 +183,11 @@ export declare type OakComponentOnlyMethods = {
};
export declare type OakComponentData<ED extends EntityDict & BaseEntityDict, T extends keyof ED> = {
oakExecuting: boolean;
oakFocused: object;
oakAllowExecuting: boolean | OakUserException;
oakFocused: {
attr: string;
message: string;
};
oakDirty: boolean;
oakLoading: boolean;
oakLoadingMore: boolean;

View File

@ -245,7 +245,7 @@ function mergeOperation<ED extends EntityDict & BaseEntityDict, T extends keyof
default: {
// update/remove只合并filter完全相同的项
const { filter: filter2, data: data2 } = oper2;
const { filter} = oper as ED[T]['Remove'];
const { filter } = oper as ED[T]['Remove'];
assert(filter && filter2, '更新动作目前应该都有谓词条件');
if (same(entity, schema, filter, filter2)) {
mergeOperationData(entity, schema, data, data2);
@ -340,7 +340,7 @@ class ListNode<
if (child === child2) {
return `${idx}`;
}
idx ++;
idx++;
}
assert(false);
@ -702,7 +702,7 @@ class ListNode<
return filter;
})
);
if (withParent) {
const filterOfParent = (this.parent as SingleNode<ED, keyof ED, Cxt, AD>)?.getOtmFilter<T>(this);
if (filterOfParent) {
@ -719,7 +719,7 @@ class ListNode<
async refresh(pageNumber?: number, getCount?: true, append?: boolean) {
const { entity, pagination } = this;
const { currentPage, pageSize } = pagination;
const { currentPage, pageSize } = pagination;
if (append) {
this.loadingMore = true;
}
@ -734,8 +734,8 @@ class ListNode<
{
data: projection,
filter: filters.length > 0
? combineFilters(filters)
: undefined,
? combineFilters(filters)
: undefined,
sorter,
indexFrom: currentPage3 * pageSize,
count: pageSize,
@ -934,6 +934,11 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
entity: keyof ED;
operation: ED[keyof ED]['Operation'];
}>;
const [ operation ] = this.operations;
let id = this.id;
if (operation?.oper.action === 'create') {
id = (operation.oper.data as ED[T]['CreateSingle']['data']).id;
}
operations.push(...this.operations.map(
ele => ({
entity: this.entity,
@ -943,7 +948,7 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
const { result } = await this.cache.tryRedoOperationsThenSelect(this.entity, {
data: projection,
filter: {
id: this.id,
id,
} as any,
}, operations);
return result[0];
@ -965,12 +970,22 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
beforeExecute,
afterExecute,
};
const merged = mergeOperation(this.entity, this.schema, operation, this.operations);
if (!merged) {
assert(this.operations.length === 0); // singleNode上的merge应该不可能失败所有的操作都是基于id的
if (this.operations.length === 0) {
// 处理一下create
if (oper.action === 'create') {
Object.assign(oper.data, {
id: await generateNewId(),
});
}
Object.assign(oper, { id: await generateNewId() });
this.operations.push(operation as Operation<ED, T>);
}
else {
// singleNode上应当有且只有一个operation无论什么情况
const [current] = this.operations;
Object.assign(current.oper.data, oper.data);
mergeOperationTrigger(operation, current);
}
this.setDirty();
}
@ -1474,12 +1489,13 @@ export class RunningTree<
const node = this.findNode(path);
const operations = await node.composeOperations();
if (operations) {
await this.cache.tryRedoOperations(node.getEntity(), operations.map(ele => ele.oper));
return await this.cache.tryRedoOperations(node.getEntity(), operations.map(ele => ele.oper));
}
return false;
}
@Action
async execute(path: string) {
async execute(path: string) {
const node = this.findNode(path);
if (!node.isDirty()) {
return;
@ -1488,7 +1504,7 @@ export class RunningTree<
node.setExecuting(true);
try {
const operations = await node.composeOperations() as Operation<ED, keyof ED>[];
for (const operation of operations) {
operation.beforeExecute && await operation.beforeExecute();
}

View File

@ -4,6 +4,7 @@ import {
EntityDict,
OakException,
OakInputIllegalException,
OakUserException,
} from 'oak-domain/lib/types';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { subscribe as FeactureSubscribe } from './types/Feature';
@ -168,6 +169,22 @@ export async function reRender<
if (extra) {
Object.assign(data, extra);
}
let oakAllowExecuting: boolean | OakUserException = false;
try {
oakAllowExecuting = await this.features.runningTree.tryExecute(this.state.oakFullpath);
}
catch (err) {
if (err instanceof OakUserException) {
oakAllowExecuting = err;
}
else {
oakAllowExecuting = false;
throw err;
}
}
Object.assign(data, {
oakAllowExecuting,
});
this.setState(data);
} else {
const data: Record<string, any> = formData
@ -227,47 +244,41 @@ export async function execute<
return;
}
this.setState({
oakFocused: {},
oakFocused: undefined,
oakExecuting: true,
});
try {
const fullpath = path
? `${this.state.oakFullpath}.${path}`
: this.state.oakFullpath;
const result = await this.features.runningTree.execute(fullpath);
this.setState({
oakExecuting: false,
})
this.setMessage({
type: 'success',
content: '操作成功',
});
return result;
} catch (err) {
if (err instanceof OakException) {
if (err instanceof OakUserException) {
if (err instanceof OakInputIllegalException) {
const attr = err.getAttributes()[0];
const attrs = err.getAttributes();
const message = err.message;
this.setState({
oakFocused: {
[attr]: true,
attr: attrs[0],
message,
},
oakExecuting: false,
});
this.setMessage({
type: 'warning',
content: err.message,
});
} else {
const { name } = err.constructor;
if (legalExceptions && legalExceptions.includes(name)) {
// 如果调用时就知道有异常,直接抛出
this.setState({
oakExecuting: false,
});
throw err;
}
return;
}
} else {
this.setMessage({
type: 'warning',
content: (err as Error).message,
});
}
this.setMessage({
type: 'error',
content: (err as Error).message,
});
throw err;
}
}
@ -315,14 +326,11 @@ export async function setUpdateData<
} as ED[T]['Update']);
}
else {
const id = await generateNewId();
await this.addOperation({
action: 'create',
data: {
id,
[attr]: data,
}
} as ED[T]['CreateSingle']);
this.setProps({ oakId: id });
}
}

View File

@ -1,4 +1,4 @@
import { Aspect, Context, EntityDict, DeduceSorterItem, DeduceOperation, SelectRowShape, CheckerType } from "oak-domain/lib/types";
import { Aspect, Context, EntityDict, DeduceSorterItem, DeduceOperation, SelectRowShape, CheckerType, OakException, OakUserException } from "oak-domain/lib/types";
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { CommonAspectDict } from 'oak-common-aspect';
import { Feature } from './Feature';
@ -298,7 +298,11 @@ export type OakComponentOnlyMethods = {
export type OakComponentData<ED extends EntityDict & BaseEntityDict, T extends keyof ED> = {
oakExecuting: boolean;
oakFocused: object;
oakAllowExecuting: boolean | OakUserException;
oakFocused: {
attr: string;
message: string;
};
oakDirty: boolean;
oakLoading: boolean;
oakLoadingMore: boolean;