server msgTimeout 支持

This commit is contained in:
2betop 2020-01-03 17:58:41 +08:00
parent 10df6f2a71
commit 94cbacef61
12 changed files with 222 additions and 102 deletions

View File

@ -65,7 +65,14 @@ export interface RendererBasicConfig {
export interface RendererEnv {
fetcher: (api: Api, data?: any, options?: object) => Promise<Payload>;
isCancel: (val: any) => boolean;
notify: (type: 'error' | 'success', msg: string) => void;
notify: (
type: 'error' | 'success',
msg: string,
conf?: {
closeButton?: boolean;
timeout?: number;
}
) => void;
jumpTo: (to: string, action?: Action, ctx?: object) => void;
alert: (msg: string) => void;
confirm: (msg: string, title?: string) => Promise<boolean>;

View File

@ -146,7 +146,16 @@ export class Chart extends React.Component<ChartProps> {
})
.then(result => {
if (!result.ok) {
return env.notify('error', result.msg || '加载失败,请重试!');
return env.notify(
'error',
result.msg || '加载失败,请重试!',
result.msgTimeout !== undefined
? {
closeButton: true,
timeout: result.msgTimeout
}
: undefined
);
}
delete this.reloadCancel;
this.renderChart(result.data || {});

View File

@ -118,6 +118,8 @@ export default class ChainedSelectControl extends React.Component<
parent: arr[idx]
})
.then(ret => {
// todo 没有检测 response.ok
const stack = this.state.stack.concat();
const remoteValue = ret.data ? ret.data.value : undefined;
let options = (ret.data && (ret.data as any).options) || ret.data;

View File

@ -109,7 +109,10 @@ export default class ComboControl extends React.Component<ComboProps> {
'tabsStyle',
'lazyLoad',
'changeImmediately',
'strictMode'
'strictMode',
'controls',
'conditions',
'messages'
];
subForms: Array<any> = [];

View File

@ -44,6 +44,8 @@ export default class FormControl extends React.PureComponent<
ControlProps,
ControlState
> {
static propsList: any = ['control'];
public model: IFormItemStore | undefined;
control: any;
value?: any;

View File

@ -197,7 +197,16 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
json.msg || options.errorMessage || '获取失败',
true
);
(getRoot(self) as IRendererStore).notify('error', json.msg);
(getRoot(self) as IRendererStore).notify(
'error',
json.msg,
json.msgTimeout !== undefined
? {
closeButton: true,
timeout: json.msgTimeout
}
: undefined
);
} else {
if (!json.data) {
throw new Error('返回数据格式不正确payload.data 没有数据');
@ -369,7 +378,16 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
json.msg || options.errorMessage || '保存失败',
true
);
(getRoot(self) as IRendererStore).notify('error', self.msg);
(getRoot(self) as IRendererStore).notify(
'error',
self.msg,
json.msgTimeout !== undefined
? {
closeButton: true,
timeout: json.msgTimeout
}
: undefined
);
throw new ServerError(self.msg);
} else {
self.updateMessage(json.msg || options.successMessage);

View File

@ -3,6 +3,7 @@ import debounce = require('lodash/debounce');
import {ServiceStore} from './service';
import {FormItemStore, IFormItemStore, SFormItemStore} from './formItem';
import {Api, fetchOptions, Payload} from '../types';
import {ServerError} from '../utils/errors';
import {
getVariable,
setVariable,
@ -19,10 +20,6 @@ import {IComboStore} from './combo';
import isEqual = require('lodash/isEqual');
import {IRendererStore} from '.';
class ServerError extends Error {
type = 'ServerError';
}
export const FormStore = ServiceStore.named('FormStore')
.props({
inited: false,
@ -97,6 +94,10 @@ export const FormStore = ServiceStore.named('FormStore')
isPristine: boolean = false,
force: boolean = false
) {
if (name === 'strategy_json') {
debugger;
}
// 没有变化就不跑了。
const origin = getVariable(self.data, name, false);
@ -265,7 +266,7 @@ export const FormStore = ServiceStore.named('FormStore')
);
}
throw new ServerError(self.msg);
throw new ServerError(self.msg, json);
} else {
if (options && options.onSuccess) {
const ret = options.onSuccess(json);
@ -288,7 +289,23 @@ export const FormStore = ServiceStore.named('FormStore')
self.markSaving(false);
// console.error(e.stack);`
(getRoot(self) as IRendererStore).notify('error', e.message);
if (e.type === 'ServerError') {
const result = (e as ServerError).response;
(getRoot(self) as IRendererStore).notify(
'error',
e.message,
result.msgTimeout !== undefined
? {
closeButton: true,
timeout: result.msgTimeout
}
: undefined
);
} else {
(getRoot(self) as IRendererStore).notify('error', e.message);
}
throw e;
}
});

View File

@ -5,26 +5,26 @@ import {
flow,
getRoot,
hasParent
} from "mobx-state-tree";
import { IFormStore } from "./form";
import { str2rules, validate as doValidate } from "../utils/validations";
import { Api, Payload, fetchOptions } from "../types";
import { ComboStore, IComboStore, IUniqueGroup } from "./combo";
import { evalExpression } from "../utils/tpl";
import findIndex = require("lodash/findIndex");
} from 'mobx-state-tree';
import {IFormStore} from './form';
import {str2rules, validate as doValidate} from '../utils/validations';
import {Api, Payload, fetchOptions} from '../types';
import {ComboStore, IComboStore, IUniqueGroup} from './combo';
import {evalExpression} from '../utils/tpl';
import findIndex = require('lodash/findIndex');
import {
isArrayChildrenModified,
isObject,
createObject,
isObjectShallowModified,
findTree
} from "../utils/helper";
import { flattenTree } from "../utils/helper";
import { IRendererStore } from ".";
import { normalizeOptions, optionValueCompare } from "../components/Select";
import find = require("lodash/find");
import { SimpleMap } from "../utils/SimpleMap";
import memoize = require("lodash/memoize");
} from '../utils/helper';
import {flattenTree} from '../utils/helper';
import {IRendererStore} from '.';
import {normalizeOptions, optionValueCompare} from '../components/Select';
import find = require('lodash/find');
import {SimpleMap} from '../utils/SimpleMap';
import memoize = require('lodash/memoize');
interface IOption {
value?: string | number | null;
@ -35,16 +35,16 @@ interface IOption {
hidden?: boolean | null;
}
const ErrorDetail = types.model("ErrorDetail", {
msg: "",
tag: ""
const ErrorDetail = types.model('ErrorDetail', {
msg: '',
tag: ''
});
export const FormItemStore = types
.model("FormItemStore", {
.model('FormItemStore', {
identifier: types.identifier,
isFocused: false,
type: "",
type: '',
unique: false,
loading: false,
required: false,
@ -52,14 +52,14 @@ export const FormItemStore = types
messages: types.optional(types.frozen(), {}),
errorData: types.optional(types.array(ErrorDetail), []),
name: types.string,
id: "", // 因为 name 可能会重名,所以加个 id 进来,如果有需要用来定位具体某一个
id: '', // 因为 name 可能会重名,所以加个 id 进来,如果有需要用来定位具体某一个
unsetValueOnInvisible: false,
validated: false,
validating: false,
multiple: false,
delimiter: ",",
valueField: "value",
labelField: "label",
delimiter: ',',
valueField: 'value',
labelField: 'label',
joinValues: true,
extractValue: false,
options: types.optional(types.array(types.frozen()), []),
@ -84,7 +84,7 @@ export const FormItemStore = types
return self.selectedOptions[self.selectedOptions.length - 1].value;
}
return "";
return '';
}
function getErrors(): Array<string> {
@ -120,33 +120,33 @@ export const FormItemStore = types
},
getSelectedOptions: (value: any = getValue()) => {
if (typeof value === "undefined") {
if (typeof value === 'undefined') {
return [];
}
const selected = Array.isArray(value)
? value.map(item =>
item && item.hasOwnProperty(self.valueField || "value")
? item[self.valueField || "value"]
item && item.hasOwnProperty(self.valueField || 'value')
? item[self.valueField || 'value']
: item
)
: typeof value === "string"
? value.split(self.delimiter || ",")
: typeof value === 'string'
? value.split(self.delimiter || ',')
: [
value && value.hasOwnProperty(self.valueField || "value")
? value[self.valueField || "value"]
value && value.hasOwnProperty(self.valueField || 'value')
? value[self.valueField || 'value']
: value
];
// 保留原来的 label 信息,如果原始值中有 label。
if (
value &&
value.hasOwnProperty(self.labelField || "label") &&
!selected[0].hasOwnProperty(self.labelField || "label")
value.hasOwnProperty(self.labelField || 'label') &&
!selected[0].hasOwnProperty(self.labelField || 'label')
) {
selected[0] = {
[self.labelField || "label"]: value[self.labelField || "label"],
[self.valueField || "value"]: value[self.valueField || "value"]
[self.labelField || 'label']: value[self.labelField || 'label'],
[self.valueField || 'value']: value[self.valueField || 'value']
};
}
@ -155,7 +155,7 @@ export const FormItemStore = types
selected.forEach((item, index) => {
const matched = findTree(
self.filteredOptions,
optionValueCompare(item, self.valueField || "value")
optionValueCompare(item, self.valueField || 'value')
);
if (matched) {
@ -165,11 +165,11 @@ export const FormItemStore = types
if (
unMatched &&
(typeof unMatched === "string" || typeof unMatched === "number")
(typeof unMatched === 'string' || typeof unMatched === 'number')
) {
unMatched = {
[self.valueField || "value"]: item,
[self.labelField || "label"]: item
[self.valueField || 'value']: item,
[self.labelField || 'label']: item
};
}
@ -204,8 +204,8 @@ export const FormItemStore = types
required?: any;
unique?: any;
value?: any;
rules?: string | { [propName: string]: any };
messages?: { [propName: string]: string };
rules?: string | {[propName: string]: any};
messages?: {[propName: string]: string};
multiple?: boolean;
delimiter?: string;
valueField?: string;
@ -215,25 +215,25 @@ export const FormItemStore = types
type?: string;
id?: string;
}) {
if (typeof rules === "string") {
if (typeof rules === 'string') {
rules = str2rules(rules);
}
typeof type !== "undefined" && (self.type = type);
typeof id !== "undefined" && (self.id = id);
typeof messages !== "undefined" && (self.messages = messages);
typeof required !== "undefined" && (self.required = !!required);
typeof unique !== "undefined" && (self.unique = !!unique);
typeof multiple !== "undefined" && (self.multiple = !!multiple);
typeof joinValues !== "undefined" && (self.joinValues = !!joinValues);
typeof extractValue !== "undefined" &&
typeof type !== 'undefined' && (self.type = type);
typeof id !== 'undefined' && (self.id = id);
typeof messages !== 'undefined' && (self.messages = messages);
typeof required !== 'undefined' && (self.required = !!required);
typeof unique !== 'undefined' && (self.unique = !!unique);
typeof multiple !== 'undefined' && (self.multiple = !!multiple);
typeof joinValues !== 'undefined' && (self.joinValues = !!joinValues);
typeof extractValue !== 'undefined' &&
(self.extractValue = !!extractValue);
typeof delimiter !== "undefined" &&
(self.delimiter = (delimiter as string) || ",");
typeof valueField !== "undefined" &&
(self.valueField = (valueField as string) || "value");
typeof labelField !== "undefined" &&
(self.labelField = (labelField as string) || "label");
typeof delimiter !== 'undefined' &&
(self.delimiter = (delimiter as string) || ',');
typeof valueField !== 'undefined' &&
(self.valueField = (valueField as string) || 'value');
typeof labelField !== 'undefined' &&
(self.labelField = (labelField as string) || 'label');
rules = rules || {};
rules = {
@ -243,7 +243,7 @@ export const FormItemStore = types
if (isObjectShallowModified(rules, self.rules)) {
self.rules = rules;
clearError("bultin");
clearError('bultin');
self.validated = false;
}
@ -261,7 +261,7 @@ export const FormItemStore = types
}
function changeValue(value: any, isPrintine: boolean = false) {
if (typeof value === "undefined" || value === "__undefined") {
if (typeof value === 'undefined' || value === '__undefined') {
self.form.deleteValueByName(self.name);
} else {
self.form.setValueByName(self.name, value, isPrintine);
@ -289,7 +289,7 @@ export const FormItemStore = types
if (
self.unique &&
self.form.parentStore &&
self.form.parentStore.storeType === "ComboStore"
self.form.parentStore.storeType === 'ComboStore'
) {
const combo = self.form.parentStore as IComboStore;
const group = combo.uniques.get(self.name) as IUniqueGroup;
@ -307,12 +307,12 @@ export const FormItemStore = types
return self.valid;
});
function setError(msg: string | Array<string>, tag: string = "bultin") {
function setError(msg: string | Array<string>, tag: string = 'bultin') {
clearError();
addError(msg, tag);
}
function addError(msg: string | Array<string>, tag: string = "bultin") {
function addError(msg: string | Array<string>, tag: string = 'bultin') {
const msgs: Array<string> = Array.isArray(msg) ? msg : [msg];
msgs.forEach(item =>
self.errorData.push({
@ -385,8 +385,14 @@ export const FormItemStore = types
(options && options.errorMessage)}`
);
(getRoot(self) as IRendererStore).notify(
"error",
self.errors.join("")
'error',
self.errors.join(''),
json.msgTimeout !== undefined
? {
closeButton: true,
timeout: json.msgTimeout
}
: undefined
);
} else {
clearError();
@ -401,12 +407,12 @@ export const FormItemStore = types
options = normalizeOptions(options as any);
setOptions(options);
if (json.data && typeof (json.data as any).value !== "undefined") {
if (json.data && typeof (json.data as any).value !== 'undefined') {
onChange && onChange((json.data as any).value, false, true);
} else if (clearValue) {
self.selectedOptions.some((item: any) => item.__unmatched) &&
onChange &&
onChange("", false, true);
onChange('', false, true);
}
}
@ -414,7 +420,7 @@ export const FormItemStore = types
return json;
} catch (e) {
const root = getRoot(self) as IRendererStore;
if (root.storeType !== "RendererStore") {
if (root.storeType !== 'RendererStore') {
// 已经销毁了,不管这些数据了。
return;
}
@ -427,13 +433,13 @@ export const FormItemStore = types
console.error(e.stack);
getRoot(self) &&
(getRoot(self) as IRendererStore).notify("error", e.message);
(getRoot(self) as IRendererStore).notify('error', e.message);
return null;
}
} as any);
function syncOptions(originOptions?: Array<any>) {
if (!self.options.length && typeof self.value === "undefined") {
if (!self.options.length && typeof self.value === 'undefined') {
self.selectedOptions = [];
self.filteredOptions = [];
return;
@ -443,24 +449,24 @@ export const FormItemStore = types
const value = self.value;
const selected = Array.isArray(value)
? value.map(item =>
item && item.hasOwnProperty(self.valueField || "value")
? item[self.valueField || "value"]
item && item.hasOwnProperty(self.valueField || 'value')
? item[self.valueField || 'value']
: item
)
: typeof value === "string"
? value.split(self.delimiter || ",")
: typeof value === 'string'
? value.split(self.delimiter || ',')
: value === void 0
? []
: [
value && value.hasOwnProperty(self.valueField || "value")
? value[self.valueField || "value"]
value && value.hasOwnProperty(self.valueField || 'value')
? value[self.valueField || 'value']
: value
];
if (value && value.hasOwnProperty(self.labelField || "label")) {
if (value && value.hasOwnProperty(self.labelField || 'label')) {
selected[0] = {
[self.labelField || "label"]: value[self.labelField || "label"],
[self.valueField || "value"]: value[self.valueField || "value"]
[self.labelField || 'label']: value[self.labelField || 'label'],
[self.valueField || 'value']: value[self.valueField || 'value']
};
}
@ -499,7 +505,7 @@ export const FormItemStore = types
selected.forEach((item, index) => {
let idx = findIndex(
flattened,
optionValueCompare(item, self.valueField || "value")
optionValueCompare(item, self.valueField || 'value')
);
if (~idx) {
@ -509,24 +515,24 @@ export const FormItemStore = types
if (
unMatched &&
(typeof unMatched === "string" || typeof unMatched === "number")
(typeof unMatched === 'string' || typeof unMatched === 'number')
) {
unMatched = {
[self.valueField || "value"]: item,
[self.labelField || "label"]: item,
__unmatched: true
[self.valueField || 'value']: item,
[self.labelField || 'label']: item,
'__unmatched': true
};
const orgin: any =
originOptions &&
find(
originOptions,
optionValueCompare(item, self.valueField || "value")
optionValueCompare(item, self.valueField || 'value')
);
if (orgin) {
unMatched[self.labelField || "label"] =
orgin[self.labelField || "label"];
unMatched[self.labelField || 'label'] =
orgin[self.labelField || 'label'];
}
}
@ -572,7 +578,7 @@ export const FormItemStore = types
function reset() {
self.validated = false;
if (subStore && subStore.storeType === "ComboStore") {
if (subStore && subStore.storeType === 'ComboStore') {
const combo = subStore as IComboStore;
combo.forms.forEach(form => form.reset());
}

View File

@ -3,6 +3,7 @@ import {iRendererStore} from './iRenderer';
import {IRendererStore} from './index';
import {Api, ApiObject, Payload, fetchOptions} from '../types';
import {extendObject, isEmpty} from '../utils/helper';
import {ServerError} from '../utils/errors';
export const ServiceStore = iRendererStore
.named('ServiceStore')
@ -85,7 +86,16 @@ export const ServiceStore = iRendererStore
if (!json.ok) {
updateMessage(json.msg || (options && options.errorMessage), true);
(getRoot(self) as IRendererStore).notify('error', json.msg);
(getRoot(self) as IRendererStore).notify(
'error',
json.msg,
json.msgTimeout !== undefined
? {
closeButton: true,
timeout: json.msgTimeout
}
: undefined
);
} else {
reInitData({
...self.data,
@ -165,7 +175,16 @@ export const ServiceStore = iRendererStore
if (!json.ok) {
updateMessage(json.msg || (options && options.errorMessage), true);
(getRoot(self) as IRendererStore).notify('error', self.msg);
(getRoot(self) as IRendererStore).notify(
'error',
self.msg,
json.msgTimeout !== undefined
? {
closeButton: true,
timeout: json.msgTimeout
}
: undefined
);
} else {
if (options && options.onSuccess) {
const ret = options.onSuccess(json);
@ -238,7 +257,7 @@ export const ServiceStore = iRendererStore
json.msg || (options && options.errorMessage) || '保存失败',
true
);
throw new Error(self.msg);
throw new ServerError(self.msg, json);
} else {
if (options && options.onSuccess) {
const ret = options.onSuccess(json);
@ -258,7 +277,21 @@ export const ServiceStore = iRendererStore
} catch (e) {
self.saving = false;
// console.log(e.stack);
(getRoot(self) as IRendererStore).notify('error', e.message || e);
if (e.type === 'ServerError') {
const result = (e as ServerError).response;
(getRoot(self) as IRendererStore).notify(
'error',
e.message,
result.msgTimeout !== undefined
? {
closeButton: true,
timeout: result.msgTimeout
}
: undefined
);
} else {
(getRoot(self) as IRendererStore).notify('error', e.message);
}
throw e;
}
@ -316,7 +349,16 @@ export const ServiceStore = iRendererStore
json.msg || (options && options.errorMessage) || '获取失败,请重试',
true
);
(getRoot(self) as IRendererStore).notify('error', self.msg);
(getRoot(self) as IRendererStore).notify(
'error',
self.msg,
json.msgTimeout !== undefined
? {
closeButton: true,
timeout: json.msgTimeout
}
: undefined
);
} else {
if (json.data) {
self.schema = json.data;

View File

@ -23,6 +23,7 @@ export interface fetcherResult {
data: object;
status: number;
msg: string;
msgTimeout?: number;
errors?: {
[propName: string]: string;
};
@ -45,6 +46,7 @@ export interface fetchOptions {
export interface Payload {
ok: boolean;
msg: string;
msgTimeout?: number;
data: any;
status: number;
errors?: {

View File

@ -161,6 +161,7 @@ function responseAdaptor(ret: fetcherResult) {
ok: data.status == 0,
status: data.status,
msg: data.msg,
msgTimeout: data.msgTimeout,
data: data.data
};

11
src/utils/errors.ts Normal file
View File

@ -0,0 +1,11 @@
import {Payload} from '../types';
export class ServerError extends Error {
type = 'ServerError';
response: Payload;
constructor(msg: string, response: Payload) {
super(msg);
this.response = response;
}
}