事务泄露的问题,以及在checkOperation中发现缺失外键的处理

This commit is contained in:
Xu Chang 2024-03-28 13:53:32 +08:00
parent 7a3c78cadf
commit 9c2a94d589
22 changed files with 204 additions and 169 deletions

View File

@ -1,9 +1,9 @@
export default OakComponent({
isList: false,
data: {
slideWidth: 0,
slideLeft: 0,
slideShow: false,
slideWidth: 0, //小程序使用
slideLeft: 0, //小程序使用
slideShow: false, //小程序使用
commonAction: [
'create',
'update',

View File

@ -63,7 +63,7 @@ export default OakComponent({
let currentUrl = event.currentTarget.dataset.src;
let urlList = event.currentTarget.dataset.list;
wx.previewImage({
current: currentUrl,
current: currentUrl, // 当前显示图片的http链接
urls: urlList, // 需要预览的图片http链接列表
});
},

View File

@ -37,7 +37,7 @@ export default OakComponent({
code: '',
title: '',
desc: '',
icon: '',
icon: '', //web独有
imagePath: '', //小程序独有
},
lifetimes: {

View File

@ -90,7 +90,7 @@ export default OakComponent({
attribute: {},
options: [],
inputType: '',
timeStartStr: '',
timeStartStr: '', // 小程序选择时间显示
timeEndStr: '',
selectedLabel: '',
minDateMp: new Date(1980, 1, 1).getTime(),

View File

@ -19,5 +19,6 @@ declare const _default: <ED2 extends ED, T2 extends keyof ED2>(props: ReactCompo
} | undefined;
hideHeader: boolean;
size?: "small" | "large" | "middle" | undefined;
scroll?: TableProps<RowWithActions<ED, keyof ED>[]>['scroll'];
}>) => React.ReactElement;
export default _default;

View File

@ -14,6 +14,7 @@ export default OakComponent({
rowSelection: {},
hideHeader: false,
size: 'large',
scroll: {},
},
formData({ props }) {
const { converter } = this.state;

View File

@ -21,4 +21,5 @@ export default function Render(props: WebComponentProps<ED, keyof ED, false, {
hideHeader?: boolean;
judgeAttributes: OakAbsAttrJudgeDef[];
size?: 'large' | 'middle' | 'small';
scroll?: TableProps<any[]>['scroll'];
}, {}>): React.JSX.Element;

View File

@ -9,7 +9,7 @@ import { TableContext } from '../listPro';
export default function Render(props) {
const { methods, data: oakData } = props;
const { t } = methods;
const { loading, entity, schema, extraActions, data, colorDict, disabledOp = false, tablePagination, onAction, rowSelection, attributes, i18n, hideHeader, judgeAttributes, size = 'large', } = oakData;
const { loading, entity, schema, extraActions, data, colorDict, disabledOp = false, tablePagination, onAction, rowSelection, attributes, i18n, hideHeader, judgeAttributes, size = 'large', scroll, } = oakData;
const [tableColumns, setTableColumns] = useState([]);
const { tableAttributes, setSchema } = useContext(TableContext);
// 为了i18更新时能够重新渲染
@ -112,12 +112,10 @@ export default function Render(props) {
rowSelection?.onChange &&
rowSelection?.onChange(selectedRowKeys, row, info);
},
}} size={size} loading={loading} dataSource={data} columns={tableColumns} pagination={tablePagination} scroll={showScroll
? {
scrollToFirstRowOnChange: true,
x: 1200,
}
: {}} onRow={(record) => {
}} size={size} loading={loading} dataSource={data} columns={tableColumns} pagination={tablePagination} scroll={Object.assign({}, showScroll && {
scrollToFirstRowOnChange: true,
x: 1200,
}, scroll)} onRow={(record) => {
return {
onClick: () => {
const index = selectedRowKeys.findIndex((ele) => ele === record.id);

View File

@ -26,6 +26,7 @@ type Props<ED2 extends ED, T extends keyof ED2> = {
};
disableSerialNumber?: boolean;
size?: 'large' | 'middle' | 'small';
scroll?: TableProps<RowWithActions<ED2, T>[]>['scroll'];
};
export type TableAttributeType = {
attribute: OakAbsAttrJudgeDef;

View File

@ -15,7 +15,7 @@ export const TableContext = createContext({
onReset: undefined,
});
const ProList = (props) => {
const { buttonGroup, entity, extraActions, onAction, disabledOp, attributes, data, loading, tablePagination, rowSelection, onReload, disableSerialNumber, title, hideDefaultButtons = false, extraContent, size = 'large', } = props;
const { buttonGroup, entity, extraActions, onAction, disabledOp, attributes, data, loading, tablePagination, rowSelection, onReload, disableSerialNumber, title, hideDefaultButtons = false, extraContent, size = 'large', scroll, } = props;
const features = useFeatures();
const [tableAttributes, setTableAttributes] = useState([]);
const [schema, setSchema] = useState(undefined);
@ -70,7 +70,7 @@ const ProList = (props) => {
<div className={Style.container}>
{!isMobile && !hideDefaultButtons && (<ToolBar title={title} extraContent={extraContent} buttonGroup={buttonGroup} reload={onReload}/>)}
{isMobile && <ButtonGroup items={buttonGroup}/>}
<List entity={entity} extraActions={extraActions} onAction={onAction} disabledOp={disabledOp} attributes={attributes} data={!disableSerialNumber
<List entity={entity} extraActions={extraActions} onAction={onAction} disabledOp={disabledOp} attributes={attributes} scroll={scroll} data={!disableSerialNumber
? data?.map((ele, index) => {
if (tablePagination) {
const total = tablePagination.total || 0;

View File

@ -9,7 +9,7 @@ export default OakComponent({
multiple: false,
entityId: '',
entityIds: [],
pickerRender: {},
pickerRender: {}, // OakAbsRefAttrPickerRender
onChange: (() => undefined),
},
formData() {

View File

@ -5,7 +5,7 @@ export default OakComponent({
return this.props.entity;
},
properties: {
helps: {},
helps: {}, // Record<string, string>;
entity: '',
attributes: [],
layout: 'horizontal',

View File

@ -94,7 +94,7 @@ export declare class Cache<ED extends EntityDict & BaseEntityDict, Cxt extends A
action: ED[T]['Action'];
data?: ED[T]['Operation']['data'];
filter?: ED[T]['Operation']['filter'];
}, checkerTypes?: CheckerType[]): true | OakUserException<ED>;
}, checkerTypes?: CheckerType[]): boolean | OakUserException<ED>;
redoOperation(opers: Array<{
entity: keyof ED;
operation: ED[keyof ED]['Operation'];

View File

@ -346,6 +346,11 @@ export class Cache extends Feature {
if (autoCommit) {
this.rollback();
}
if (err instanceof OakRowUnexistedException) {
// 有外键缺失,尝试发一下请求
this.fetchRows(err.getRows());
return false;
}
if (!(err instanceof OakUserException)) {
throw err;
}
@ -357,7 +362,7 @@ export class Cache extends Feature {
opers.forEach((oper) => {
const { entity, operation } = oper;
this.cacheStore.operate(entity, operation, this.context, {
checkerTypes: ['logicalData', 'logical'],
checkerTypes: ['logicalData', 'logical'], // 这里不能检查data不然在数据没填完前会有大量异常
dontCollect: true,
});
});

View File

@ -406,59 +406,65 @@ export function reRender(option, extra) {
if (this.state.oakEntity && this.state.oakFullpath) {
// 现在取数据需要把runningTree上的更新应用了再取判定actions也一样
this.features.runningTree.redoBranchOperations(this.state.oakFullpath);
const rows = this.features.runningTree.getFreshValue(this.state.oakFullpath);
const oakLegalActions = rows && checkActionsAndCascadeEntities.call(this, rows, option);
this.features.runningTree.rollbackRedoBranchOperations();
const oakDirty = this.features.runningTree.isDirty(this.state.oakFullpath);
const oakLoadingMore = this.features.runningTree.isLoadingMore(this.state.oakFullpath);
const oakLoading = !oakLoadingMore && this.features.runningTree.isLoading(this.state.oakFullpath);
const oakExecuting = this.features.runningTree.isExecuting(this.state.oakFullpath);
const oakExecutable = !oakExecuting && this.tryExecute();
let data = formData
? formData.call(this, {
data: rows,
features,
props: this.props,
legalActions: oakLegalActions,
})
: {};
Object.assign(data, {
oakLegalActions,
oakLocales: localeState.dataset,
oakLocalesVersion: localeState.version,
oakLng: localeState.lng,
oakDefaultLng: localeState.defaultLng,
});
if (option.isList) {
// 因为oakFilters和props里的oakFilters同名这里只能先注掉好像还没有组件用过
// const oakFilters = (this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>).getFilters();
// const oakSorters = (this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>).getSorters();
const oakPagination = this.getPagination();
try {
const rows = this.features.runningTree.getFreshValue(this.state.oakFullpath);
const oakLegalActions = rows && checkActionsAndCascadeEntities.call(this, rows, option);
const oakDirty = this.features.runningTree.isDirty(this.state.oakFullpath);
const oakLoadingMore = this.features.runningTree.isLoadingMore(this.state.oakFullpath);
const oakLoading = !oakLoadingMore && this.features.runningTree.isLoading(this.state.oakFullpath);
const oakExecuting = this.features.runningTree.isExecuting(this.state.oakFullpath);
const oakExecutable = !oakExecuting && this.tryExecute();
let data = formData
? formData.call(this, {
data: rows,
features,
props: this.props,
legalActions: oakLegalActions,
})
: {};
Object.assign(data, {
// oakFilters,
// oakSorters,
oakPagination,
oakLegalActions,
oakLocales: localeState.dataset,
oakLocalesVersion: localeState.version,
oakLng: localeState.lng,
oakDefaultLng: localeState.defaultLng,
});
}
for (const k in data) {
if (data[k] === undefined) {
if (option.isList) {
// 因为oakFilters和props里的oakFilters同名这里只能先注掉好像还没有组件用过
// const oakFilters = (this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>).getFilters();
// const oakSorters = (this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>).getSorters();
const oakPagination = this.getPagination();
Object.assign(data, {
[k]: null,
// oakFilters,
// oakSorters,
oakPagination,
});
}
for (const k in data) {
if (data[k] === undefined) {
Object.assign(data, {
[k]: null,
});
}
}
;
Object.assign(data, {
oakExecutable,
oakDirty,
oakLoading,
oakLoadingMore,
oakExecuting,
});
if (extra) {
Object.assign(data, extra);
}
this.setState(data);
this.features.runningTree.rollbackRedoBranchOperations();
}
;
Object.assign(data, {
oakExecutable,
oakDirty,
oakLoading,
oakLoadingMore,
oakExecuting,
});
if (extra) {
Object.assign(data, extra);
catch (err) {
this.features.runningTree.rollbackRedoBranchOperations();
throw err;
}
this.setState(data);
}
else {
const data = formData

View File

@ -1,7 +1,7 @@
import React from 'react';
export const keys = ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'];
export const values = {
xs: 576,
xs: 576, //小于576
sm: 576,
md: 768,
lg: 992,

View File

@ -94,7 +94,7 @@ export declare class Cache<ED extends EntityDict & BaseEntityDict, Cxt extends A
action: ED[T]['Action'];
data?: ED[T]['Operation']['data'];
filter?: ED[T]['Operation']['filter'];
}, checkerTypes?: CheckerType[]): true | OakUserException<ED>;
}, checkerTypes?: CheckerType[]): boolean | OakUserException<ED>;
redoOperation(opers: Array<{
entity: keyof ED;
operation: ED[keyof ED]['Operation'];

View File

@ -349,6 +349,11 @@ class Cache extends Feature_1.Feature {
if (autoCommit) {
this.rollback();
}
if (err instanceof Exception_1.OakRowUnexistedException) {
// 有外键缺失,尝试发一下请求
this.fetchRows(err.getRows());
return false;
}
if (!(err instanceof Exception_1.OakUserException)) {
throw err;
}
@ -360,7 +365,7 @@ class Cache extends Feature_1.Feature {
opers.forEach((oper) => {
const { entity, operation } = oper;
this.cacheStore.operate(entity, operation, this.context, {
checkerTypes: ['logicalData', 'logical'],
checkerTypes: ['logicalData', 'logical'], // 这里不能检查data不然在数据没填完前会有大量异常
dontCollect: true,
});
});

View File

@ -410,59 +410,65 @@ function reRender(option, extra) {
if (this.state.oakEntity && this.state.oakFullpath) {
// 现在取数据需要把runningTree上的更新应用了再取判定actions也一样
this.features.runningTree.redoBranchOperations(this.state.oakFullpath);
const rows = this.features.runningTree.getFreshValue(this.state.oakFullpath);
const oakLegalActions = rows && checkActionsAndCascadeEntities.call(this, rows, option);
this.features.runningTree.rollbackRedoBranchOperations();
const oakDirty = this.features.runningTree.isDirty(this.state.oakFullpath);
const oakLoadingMore = this.features.runningTree.isLoadingMore(this.state.oakFullpath);
const oakLoading = !oakLoadingMore && this.features.runningTree.isLoading(this.state.oakFullpath);
const oakExecuting = this.features.runningTree.isExecuting(this.state.oakFullpath);
const oakExecutable = !oakExecuting && this.tryExecute();
let data = formData
? formData.call(this, {
data: rows,
features,
props: this.props,
legalActions: oakLegalActions,
})
: {};
Object.assign(data, {
oakLegalActions,
oakLocales: localeState.dataset,
oakLocalesVersion: localeState.version,
oakLng: localeState.lng,
oakDefaultLng: localeState.defaultLng,
});
if (option.isList) {
// 因为oakFilters和props里的oakFilters同名这里只能先注掉好像还没有组件用过
// const oakFilters = (this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>).getFilters();
// const oakSorters = (this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>).getSorters();
const oakPagination = this.getPagination();
try {
const rows = this.features.runningTree.getFreshValue(this.state.oakFullpath);
const oakLegalActions = rows && checkActionsAndCascadeEntities.call(this, rows, option);
const oakDirty = this.features.runningTree.isDirty(this.state.oakFullpath);
const oakLoadingMore = this.features.runningTree.isLoadingMore(this.state.oakFullpath);
const oakLoading = !oakLoadingMore && this.features.runningTree.isLoading(this.state.oakFullpath);
const oakExecuting = this.features.runningTree.isExecuting(this.state.oakFullpath);
const oakExecutable = !oakExecuting && this.tryExecute();
let data = formData
? formData.call(this, {
data: rows,
features,
props: this.props,
legalActions: oakLegalActions,
})
: {};
Object.assign(data, {
// oakFilters,
// oakSorters,
oakPagination,
oakLegalActions,
oakLocales: localeState.dataset,
oakLocalesVersion: localeState.version,
oakLng: localeState.lng,
oakDefaultLng: localeState.defaultLng,
});
}
for (const k in data) {
if (data[k] === undefined) {
if (option.isList) {
// 因为oakFilters和props里的oakFilters同名这里只能先注掉好像还没有组件用过
// const oakFilters = (this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>).getFilters();
// const oakSorters = (this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>).getSorters();
const oakPagination = this.getPagination();
Object.assign(data, {
[k]: null,
// oakFilters,
// oakSorters,
oakPagination,
});
}
for (const k in data) {
if (data[k] === undefined) {
Object.assign(data, {
[k]: null,
});
}
}
;
Object.assign(data, {
oakExecutable,
oakDirty,
oakLoading,
oakLoadingMore,
oakExecuting,
});
if (extra) {
Object.assign(data, extra);
}
this.setState(data);
this.features.runningTree.rollbackRedoBranchOperations();
}
;
Object.assign(data, {
oakExecutable,
oakDirty,
oakLoading,
oakLoadingMore,
oakExecuting,
});
if (extra) {
Object.assign(data, extra);
catch (err) {
this.features.runningTree.rollbackRedoBranchOperations();
throw err;
}
this.setState(data);
}
else {
const data = formData

View File

@ -5,7 +5,7 @@ const tslib_1 = require("tslib");
const react_1 = tslib_1.__importDefault(require("react"));
exports.keys = ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'];
exports.values = {
xs: 576,
xs: 576, //小于576
sm: 576,
md: 768,
lg: 992,

View File

@ -483,6 +483,11 @@ export class Cache<
if (autoCommit) {
this.rollback();
}
if (err instanceof OakRowUnexistedException) {
// 有外键缺失,尝试发一下请求
this.fetchRows(err.getRows());
return false;
}
if (!(err instanceof OakUserException)) {
throw err;
}

View File

@ -506,70 +506,76 @@ export function reRender<
if (this.state.oakEntity && this.state.oakFullpath) {
// 现在取数据需要把runningTree上的更新应用了再取判定actions也一样
this.features.runningTree.redoBranchOperations(this.state.oakFullpath);
const rows = this.features.runningTree.getFreshValue(this.state.oakFullpath);
const oakLegalActions = rows && checkActionsAndCascadeEntities.call(
this as any,
rows,
option as any
);
this.features.runningTree.rollbackRedoBranchOperations();
try {
const rows = this.features.runningTree.getFreshValue(this.state.oakFullpath);
const oakLegalActions = rows && checkActionsAndCascadeEntities.call(
this as any,
rows,
option as any
);
const oakDirty = this.features.runningTree.isDirty(this.state.oakFullpath);
const oakLoadingMore = this.features.runningTree.isLoadingMore(this.state.oakFullpath);
const oakLoading = !oakLoadingMore && this.features.runningTree.isLoading(this.state.oakFullpath);
const oakExecuting = this.features.runningTree.isExecuting(this.state.oakFullpath);
const oakExecutable = !oakExecuting && this.tryExecute();
let data = formData
? formData.call(this, {
data: rows as RowWithActions<ED, T>,
features,
props: this.props,
legalActions: oakLegalActions,
})
: {};
const oakDirty = this.features.runningTree.isDirty(this.state.oakFullpath);
const oakLoadingMore = this.features.runningTree.isLoadingMore(this.state.oakFullpath);
const oakLoading = !oakLoadingMore && this.features.runningTree.isLoading(this.state.oakFullpath);
const oakExecuting = this.features.runningTree.isExecuting(this.state.oakFullpath);
const oakExecutable = !oakExecuting && this.tryExecute();
let data = formData
? formData.call(this, {
data: rows as RowWithActions<ED, T>,
features,
props: this.props,
legalActions: oakLegalActions,
})
: {};
Object.assign(data, {
oakLegalActions,
oakLocales: localeState.dataset,
oakLocalesVersion: localeState.version,
oakLng: localeState.lng,
oakDefaultLng: localeState.defaultLng,
});
if (option.isList) {
// 因为oakFilters和props里的oakFilters同名这里只能先注掉好像还没有组件用过
// const oakFilters = (this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>).getFilters();
// const oakSorters = (this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>).getSorters();
const oakPagination = (
this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>
).getPagination();
Object.assign(data, {
// oakFilters,
// oakSorters,
oakPagination,
oakLegalActions,
oakLocales: localeState.dataset,
oakLocalesVersion: localeState.version,
oakLng: localeState.lng,
oakDefaultLng: localeState.defaultLng,
});
}
for (const k in data) {
if (data[k] === undefined) {
if (option.isList) {
// 因为oakFilters和props里的oakFilters同名这里只能先注掉好像还没有组件用过
// const oakFilters = (this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>).getFilters();
// const oakSorters = (this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>).getSorters();
const oakPagination = (
this as ComponentFullThisType<ED, T, true, Cxt, FrontCxt>
).getPagination();
Object.assign(data, {
[k]: null,
// oakFilters,
// oakSorters,
oakPagination,
});
}
};
Object.assign(data, {
oakExecutable,
oakDirty,
oakLoading,
oakLoadingMore,
oakExecuting,
});
if (extra) {
Object.assign(data, extra);
for (const k in data) {
if (data[k] === undefined) {
Object.assign(data, {
[k]: null,
});
}
};
Object.assign(data, {
oakExecutable,
oakDirty,
oakLoading,
oakLoadingMore,
oakExecuting,
});
if (extra) {
Object.assign(data, extra);
}
this.setState(data);
this.features.runningTree.rollbackRedoBranchOperations();
}
catch (err) {
this.features.runningTree.rollbackRedoBranchOperations();
throw err;
}
this.setState(data);
} else {
const data: Record<string, any> = formData
? formData.call(this, {