fastlion-amis
Version:
一种MIS页面生成工具
2,125 lines (1,877 loc) • 54.5 kB
text/typescript
import isPlainObject from 'lodash/isPlainObject';
import isEqual from 'lodash/isEqual';
import isNaN from 'lodash/isNaN';
import uniq from 'lodash/uniq';
import last from 'lodash/last';
import { Schema, PlainObject, FunctionPropertyNames } from '../types';
import { evalExpression } from './tpl';
import qs from 'qs';
import { IIRendererStore } from '../store';
import { IFormStore } from '../store/form';
import { autobindMethod } from './autobind';
import { isObservable } from 'mobx';
import {
isPureVariable,
resolveVariable,
resolveVariableAndFilter
} from './tpl-builtin';
import MSG from '../renderers/Lion/utils/msgsub';
import { RendererEnv } from '../env';
import { isEqualWith, round } from 'lodash';
import { dealBigMoney, dealBigNumber } from './utils';
import BigNumber from 'bignumber.js';
import { isUndefined } from "lodash";
export const domUtils = {
/**
* 一个元素是否匹配一个css选择器
* @param {Element} dom
* @param {string} selector
* @return {boolean}
*/
matches(dom: Element, selector: string) {
if (dom instanceof Element && typeof selector === 'string' && selector) {
if (dom.webkitMatchesSelector) {
// 兼容android 4.4
return dom.webkitMatchesSelector(selector);
// }else if('matchesSelector' in dom){
// 兼容老版本浏览器
// return dom.matches(selector);
} else if (dom.matches) {
return dom.matches(selector);
}
}
return false;
},
/**
* 获取父级可滚动的节点
* @param node
* @returns
*/
getScrollParent(node: Element): Element | null {
if (node == null) {
return null;
}
// 兼容壳无法正确计算scrollHeight
if (node.scrollHeight > node.clientHeight && getComputedStyle(node).overflowY === 'scroll') {
return node;
} else {
return domUtils.getScrollParent(node.parentNode as HTMLElement);
}
},
/**
* 向上冒泡遍历查找与能与css选择器匹配的元素(包含自身),
* @param {HTMLElement} target
* @param {string} selector
* @param {HTMLElement} stopNode
* @return {HTMLElement}
*/
closest: function (target: HTMLElement | SVGElement, selector: string, stopNode?: HTMLElement | SVGElement): HTMLElement | SVGElement | null {
if ((target instanceof HTMLElement || target instanceof SVGElement)
&& typeof selector === 'string'
&& (isUndefined(stopNode) || stopNode instanceof HTMLElement || stopNode instanceof SVGElement)) {
let tar: HTMLElement | SVGElement | null = target;
while (tar) {
if (domUtils.matches(tar, selector)) {
return tar;
}
tar = tar.parentElement;
if (stopNode && stopNode.isSameNode(tar)) {
return null;
}
}
}
return null;
}
}
export const isNil = (value: any) => value === null || value === undefined
export function preventDefault(event: TouchEvent | Event): void {
if (typeof event.cancelable !== 'boolean' || event.cancelable) {
event.preventDefault();
}
}
export const create2DArrayByMaxSum = (arr: number[], maxSum: number) => {
let result = []; // 存储二维数组
let currentRow: number[] = []; // 存储当前行的元素
for (let num of arr) {
// 如果当前行的和加上当前数字不超过最大和,则添加到当前行
if (currentRow.reduce((sum, val) => sum + val, 0) + num <= maxSum) {
currentRow.push(num);
} else {
// 否则,将当前行添加到结果数组中,并开始新的一行
result.push(currentRow);
currentRow = [num]; // 重置当前行为新的数字
}
}
// 不要忘记在最后添加当前行(如果它不是空的)
if (currentRow.length > 0) {
result.push(currentRow);
}
return result;
}
// 仅进行一帧的方法 类似settimeout(,0)只是这个视图上更流畅
export const functionInOneFrame = (Fn: (...rest: any) => void) => {
const requestTimerId = requestAnimationFrame(() => {
Fn()
cancelAnimationFrame(requestTimerId)
})
}
/**
* 标准化展示文本 包含 前后缀 千分位 大写,金额大写 目前只识别到这三个
*/
export function standardValueText(value: string, option: { prefix?: string, suffix?: string, kilobitSeparator?: boolean, precision?: number, showUppercase?: 0 | 1 | 2, carry?: boolean }) {
if (isNil(value)) return ''
const {
prefix,
suffix,
precision,
kilobitSeparator,
showUppercase,
carry } = option
// 小数保留
if (!Number.isNaN(+value)) {
if (!isNil(precision) && typeof precision === 'number') {
if (carry) {
const val = ("" + value).toString().split('.')
value = val[0] + (val[1] ? ('.' + val[1]?.slice(0, precision)) : '')
} else
value = (+value).toFixed(precision)
}
if (kilobitSeparator && !showUppercase) {
value = numberFormatter(value, precision);
}
if (showUppercase === 1) {
value = dealBigNumber(+value)
} else if (showUppercase === 2) {
value = dealBigMoney(+value)
}
}
return (prefix ? prefix : '') + value + (suffix ? suffix : '')
}
window['_standardValueText'] = standardValueText
// 计算一下宿主的js堆允许缓存大小
export const jsMemoryHeap = (performance?.memory?.jsHeapSizeLimit / (1024 * 1024 * 1024)) || 1
// 获取最大的index大1的index值
export const getLagerThanMaxZindex = () => 1 + (Math.max(...[...Array.from(document.querySelectorAll('*')).map(_ => Number(getComputedStyle(_).getPropertyValue('z-index')))].filter(_ => (!isNaN(_) && _ < 1000000000))) as any);
let isMobileResult = /Android|ArkWeb|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
// 计算过一次就不用再计算了
export function isMobile() {
return isMobileResult
}
// 获取文本中中文个数
export const getChineseWordLengthInWord = (value: string = '') => {
return (value + '').match(/[^\u0000-\u00ff]/g)?.length || 0
}
// 获取字体的最大字节数-方便通用
export const getValueBitLength = (value: string) => {
return value.replace(/[^\u0000-\u00ff]/g, 'aa').length
}
export function range(num: number, min: number, max: number): number {
return Math.min(Math.max(num, min), max);
}
// 黑名单属性-禁止相关属性进行继承 分别是高级查询和基础查询的属性
const blackList = ['advancedFilterSub', 'advancedHeader', 'advancedFilter', 'filterOptionData', 'filterParam', 'perPage', 'page']
// 展开__super
export const flatSuperData = (data: any) => {
let curData = data
let flatObj = {}
// 当当前data有__super的时候 展开
while (curData.__super) {
flatObj = { ...curData, ...flatObj }
curData = curData.__super
}
return flatObj
}
// 方便取值的时候能够把上层的取到,但是获取的时候不会全部把所有的数据获取到。
export function createObject(
superProps?: { [propName: string]: any },
props?: { [propName: string]: any },
properties?: any,
copyBlackProPerty?: boolean
): object {
const hasBlackListKey = Object.keys(superProps || {}).find(_ => blackList.includes(_))
if (superProps && (Object.isFrozen(superProps) || hasBlackListKey)) {
superProps = cloneObject(superProps);
}
// 初始化properties
const tempSearchQuery = {}
// 如果有黑名单数据 移除掉其中的黑名单数据-防止被继承
if (hasBlackListKey)
for (const key of blackList) {
if (superProps?.hasOwnProperty(key)) {
//如果需要复制黑名单中的数据
if (copyBlackProPerty) {
// 复制黑名单数据
tempSearchQuery[key] = superProps[key]
}
delete superProps[key]
}
}
const obj = superProps
? Object.create(superProps, {
...properties,
__super: {
value: superProps,
writable: false,
enumerable: false
}
})
: Object.create(Object.prototype, properties);
// 如果需要复制,直接加到同层数据中数据中
if (hasBlackListKey) {
Object.assign(obj, tempSearchQuery)
}
props &&
isObject(props) &&
Object.keys(props).forEach(key => (obj[key] = props[key]));
return obj;
}
export function cloneObject(target: any, persistOwnProps: boolean = true) {
const obj =
target && target.__super
? Object.create(target.__super, {
__super: {
value: target.__super,
writable: false,
enumerable: false
}
})
: Object.create(Object.prototype);
persistOwnProps &&
target &&
Object.keys(target).forEach(key => (obj[key] = target[key]));
return obj;
}
/**
* 给目标对象添加其他属性,可读取但是不会被遍历。
* @param target
* @param props
*/
export function injectPropsToObject(target: any, props: any) {
const sup = Object.create(target.__super || null);
Object.keys(props).forEach(key => (sup[key] = props[key]));
const result = Object.create(sup);
Object.keys(target).forEach(key => (result[key] = target[key]));
return result;
}
export function extendObject(
target: any,
src?: any,
persistOwnProps: boolean = true
) {
const obj = cloneObject(target, persistOwnProps);
src && Object.keys(src).forEach(key => (obj[key] = src[key]));
return obj;
}
export function isSuperDataModified(
data: any,
prevData: any,
store: IIRendererStore
) {
let keys: Array<string> = [];
if (store && store.storeType === 'FormStore') {
keys = uniq(
(store as IFormStore).items
.map(item => `${item.name}`.replace(/\..*$/, ''))
.concat(Object.keys(store.data))
);
} else {
keys = Object.keys(store.data);
}
if (Array.isArray(keys) && keys.length) {
return keys.some(key => data[key] !== prevData[key]);
}
return false;
}
export function syncDataFromSuper(
data: any,
superObject: any,
prevSuperObject: any,
store: IIRendererStore,
force: boolean
) {
const obj = {
...data
};
let keys: Array<string> = [];
// 如果是 form store,则从父级同步 formItem 种东西。
if (store && store.storeType === 'FormStore') {
keys = uniq(
(store as IFormStore).items
.map(item => `${item.name}`.replace(/\..*$/, ''))
.concat(Object.keys(obj))
);
} else if (force) {
keys = Object.keys(obj);
}
if (superObject || prevSuperObject) {
keys.forEach(key => {
if (!key) {
return;
}
if (
((superObject && typeof superObject[key] !== 'undefined') ||
(prevSuperObject && typeof prevSuperObject[key] !== 'undefined')) &&
((prevSuperObject && !superObject) ||
(!prevSuperObject && superObject) ||
prevSuperObject[key] !== superObject[key])
) {
obj[key] = superObject[key];
}
});
}
return obj;
}
/**
* 生成 8 位随机数字。
*
* @return {string} 8位随机数字
*/
export function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + s4();
}
export function findIndex(
arr: Array<any>,
detect: (item?: any, index?: number) => boolean
) {
for (let i = 0, len = arr.length; i < len; i++) {
if (detect(arr[i], i)) {
return i;
}
}
return -1;
}
export function getVariable(
data: { [propName: string]: any },
key: string | undefined,
canAccessSuper: boolean = true
): any {
if (!data || !key) {
return undefined;
} else if (canAccessSuper ? key in data : data.hasOwnProperty(key)) {
return data[key];
}
return keyToPath(key).reduce(
(obj, key) =>
obj &&
typeof obj === 'object' &&
(canAccessSuper ? key in obj : obj.hasOwnProperty(key))
? obj[key]
: undefined,
data
);
}
export function setVariable(
data: { [propName: string]: any },
key: string,
value: any,
convertKeyToPath?: boolean
) {
data = data || {};
if (key in data) {
data[key] = value;
return;
}
const parts = convertKeyToPath !== false ? keyToPath(key) : [key];
const last = parts.pop() as string;
while (parts.length) {
let key = parts.shift() as string;
if (isPlainObject(data[key])) {
data = data[key] = {
...data[key]
};
} else if (Array.isArray(data[key])) {
data[key] = data[key].concat();
data = data[key];
} else if (data[key]) {
// throw new Error(`目标路径不是纯对象,不能覆盖`);
// 强行转成对象
data[key] = {};
data = data[key];
} else {
data[key] = {};
data = data[key];
}
}
data[last] = value;
}
export function deleteVariable(data: { [propName: string]: any }, key: string) {
if (!data) {
return;
} else if (data.hasOwnProperty(key)) {
delete data[key];
return;
}
const parts = keyToPath(key);
const last = parts.pop() as string;
while (parts.length) {
let key = parts.shift() as string;
if (isPlainObject(data[key])) {
data = data[key] = {
...data[key]
};
} else if (data[key]) {
throw new Error(`目标路径不是纯对象,不能修改`);
} else {
break;
}
}
if (data && data.hasOwnProperty && data.hasOwnProperty(last)) {
delete data[last];
}
}
export function hasOwnProperty(
data: { [propName: string]: any },
key: string
): boolean {
const parts = keyToPath(key);
while (parts.length) {
let key = parts.shift() as string;
if (!isObject(data) || !data.hasOwnProperty(key)) {
return false;
}
data = data[key];
}
return true;
}
export function noop() { }
export function anyChanged(
attrs: string | Array<string>,
from: { [propName: string]: any },
to: { [propName: string]: any },
strictMode: boolean = true
): boolean {
return (typeof attrs === 'string' ? attrs.split(/\s*,\s*/) : attrs).some(
key => (strictMode ? from?.[key] !== to?.[key] : from?.[key] != to?.[key])
);
}
export function getChangedProp(
attrs: string | Array<string>,
from: { [propName: string]: any },
to: { [propName: string]: any },
strictMode: boolean = true
): { key: string, fromValue: any, toValue: any }[] {
return (typeof attrs === 'string' ? attrs.split(/\s*,\s*/) : attrs).filter(
key => (strictMode ? from?.[key] !== to?.[key] : from?.[key] != to?.[key])
).map(key => ({
key,
fromValue: from?.[key],
toValue: to?.[key],
}));
}
export function rmUndefined(obj: PlainObject) {
const newObj: PlainObject = {};
if (typeof obj !== 'object') {
return obj;
}
const keys = Object.keys(obj);
keys.forEach(key => {
if (obj[key] !== undefined) {
newObj[key] = obj[key];
}
});
return newObj;
}
// 原来的,在非嚴格模式下可以使用,因為isequal會嚴格比較,留下做个对照
export function oldIsObjectShallowModified(
prev: any,
next: any,
strictMode: boolean = true,
ignoreUndefined: boolean = false,
): boolean {
if (Array.isArray(prev) && Array.isArray(next)) {
return prev.length !== next.length
? true
: prev.some((prev, index) => {
oldIsObjectShallowModified(
prev,
next[index],
strictMode,
ignoreUndefined,
)
}
);
} else if (Number.isNaN(prev) && Number.isNaN(next)) {
return false;
} else if (
null == prev ||
null == next ||
!isObject(prev) ||
!isObject(next) ||
isObservable(prev) ||
isObservable(next)
) {
return strictMode ? prev !== next : prev != next;
}
if (ignoreUndefined) {
prev = rmUndefined(prev);
next = rmUndefined(next);
}
const keys = Object.keys(prev);
const nextKeys = Object.keys(next);
if (
keys.length !== nextKeys.length ||
keys.sort().join(',') !== nextKeys.sort().join(',')
) {
return true;
}
for (let i: number = keys.length - 1; i >= 0; i--) {
let key = keys[i];
// 跳过对元数据的比较 也没啥用比较了
if (['item', 'itemsRaw', 'sortItemsRaw', 'unSelectedItems', 'selectedItems'].includes(key)) continue
if (
oldIsObjectShallowModified(
prev[key],
next[key],
strictMode,
ignoreUndefined,
)
) {
return true;
}
}
return false;
}
export function isObjectShallowModified(
prev: any,
next: any,
strictMode: boolean = true,
ignoreUndefined: boolean = false,
): boolean {
if (strictMode)
return !isEqual(prev, next)
return !isEqualWith(prev, next, (objValue, otherValue) => {
if (isObject(objValue) && isObject(otherValue)) {
return undefined;
}
return objValue == otherValue
})
}
export function isArrayChildrenModified(
prev: Array<any>,
next: Array<any>,
strictMode: boolean = true
) {
if (!Array.isArray(prev) || !Array.isArray(next)) {
return strictMode ? prev !== next : prev != next;
}
if (prev.length !== next.length) {
return true;
}
for (let i: number = prev.length - 1; i >= 0; i--) {
if (strictMode ? prev[i] !== next[i] : prev[i] != next[i]) {
return true;
}
}
return false;
}
export function immutableExtends(to: any, from: any, deep = false) {
// 不是对象,不可以merge
if (!isObject(to) || !isObject(from)) {
return to;
}
let ret = to;
Object.keys(from).forEach(key => {
const origin = to[key];
const value = from[key];
// todo 支持深度merge
if (origin !== value) {
// 一旦有修改,就创建个新对象。
ret = ret !== to ? ret : { ...to };
ret[key] = value;
}
});
return ret;
}
// 即将抛弃
export function makeColumnClassBuild(
steps: number,
classNameTpl: string = 'col-sm-$value'
) {
let count = 12;
let step = Math.floor(count / steps);
return function (schema: Schema) {
if (
schema.columnClassName &&
/\bcol-(?:xs|sm|md|lg)-(\d+)\b/.test(schema.columnClassName)
) {
const flex = parseInt(RegExp.$1, 10);
count -= flex;
steps--;
step = Math.floor(count / steps);
return schema.columnClassName;
} else if (schema.columnClassName) {
count -= step;
steps--;
return schema.columnClassName;
}
count -= step;
steps--;
return classNameTpl.replace('$value', '' + step);
};
}
export function hasVisibleExpression(schema: {
visibleOn?: string;
hiddenOn?: string;
visible?: boolean;
hidden?: boolean;
}) {
return schema?.visibleOn || schema?.hiddenOn;
}
export function isVisible(
schema: {
visibleOn?: string;
hiddenOn?: string;
visible?: boolean;
hidden?: boolean;
},
data?: object
) {
return !(
schema?.hidden ||
schema?.visible === false ||
(schema?.hiddenOn && evalExpression(schema.hiddenOn, data) === true) ||
(schema?.visibleOn && evalExpression(schema.visibleOn, data) === false)
);
}
export function isUnfolded(
node: any,
config: {
foldedField?: string;
unfoldedField?: string;
}
): boolean {
let { foldedField, unfoldedField } = config;
unfoldedField = unfoldedField || 'unfolded';
foldedField = foldedField || 'folded';
let ret: boolean = false;
if (unfoldedField && typeof node[unfoldedField] !== 'undefined') {
ret = !!node[unfoldedField];
} else if (foldedField && typeof node[foldedField] !== 'undefined') {
ret = !node[foldedField];
}
return ret;
}
/**
* 过滤掉被隐藏的数组元素
*/
export function visibilityFilter(items: any, data?: object) {
return items.filter((item: any) => {
return isVisible(item, data);
});
}
export function isDisabled(
schema: {
disabledOn?: string;
disabled?: boolean;
},
data?: object
) {
return (
schema.disabled ||
(schema.disabledOn && evalExpression(schema.disabledOn, data))
);
}
export function hasAbility(
schema: any,
ability: string,
data?: object,
defaultValue: boolean = true
): boolean {
return schema.hasOwnProperty(ability)
? schema[ability]
: schema.hasOwnProperty(`${ability}On`)
? evalExpression(schema[`${ability}On`], data || schema)
: defaultValue;
}
export function makeHorizontalDeeper(
horizontal: {
left: string;
right: string;
offset: string;
leftFixed?: any;
},
count: number
): {
left: string | number;
right: string | number;
offset: string | number;
leftFixed?: any;
} {
if (count > 1 && /\bcol-(xs|sm|md|lg)-(\d+)\b/.test(horizontal.left)) {
const flex = parseInt(RegExp.$2, 10) * count;
return {
leftFixed: horizontal.leftFixed,
left: flex,
right: 12 - flex,
offset: flex
};
} else if (count > 1 && typeof horizontal.left === 'number') {
const flex = horizontal.left * count;
return {
leftFixed: horizontal.leftFixed,
left: flex,
right: 12 - flex,
offset: flex
};
}
return horizontal;
}
export function promisify<T extends Function>(
fn: T
): (...args: Array<any>) => Promise<any> & {
raw: T;
} {
let promisified = function () {
try {
const ret = fn.apply(null, arguments);
if (ret && ret.then) {
return ret;
} else if (typeof ret === 'function') {
// thunk support
return new Promise((resolve, reject) =>
ret((error: boolean, value: any) =>
error ? reject(error) : resolve(value)
)
);
}
return Promise.resolve(ret);
} catch (e) {
return Promise.reject(e);
}
};
(promisified as any).raw = fn;
return promisified;
}
export function getScrollParent(node: HTMLElement): HTMLElement | null {
if (node == null) {
return null;
}
const style = getComputedStyle(node);
if (!style) {
return null;
}
const text =
style.getPropertyValue('overflow') +
style.getPropertyValue('overflow-x') +
style.getPropertyValue('overflow-y');
if (/auto|scroll/.test(text) || node.nodeName === 'BODY') {
return node;
}
return getScrollParent(node.parentNode as HTMLElement);
}
// 获取文字再元素中的宽度
export const getTextWidth = (text: string, element?: HTMLElement) =>// 获取文本的长度(像素)的函数
{
// 创建一个临时的span元素来计算文本的长度
const span = document.createElement('div');
span.style.fontSize = '13px';
span.style.visibility = 'hidden';
span.style.display = 'inline';
span.style.whiteSpace = 'nowrap'; /* 禁止换行 */
span.innerHTML = text;
if (element) {
// 获取计算后的样式
const computedStyle = window.getComputedStyle(element);
// 设置span元素的样式以匹配目标元素
span.style.fontFamily = computedStyle.fontFamily;
span.style.fontSize = computedStyle.fontSize;
span.style.fontWeight = computedStyle.fontWeight;
span.style.fontStyle = computedStyle.fontStyle;
span.style.letterSpacing = computedStyle.letterSpacing;
}
// 将span元素添加到文档中
document.body.appendChild(span);
// 获取文本的宽度
const width = span.offsetWidth;
// 移除临时创建的span元素
document.body.removeChild(span);
// 匀出半个字的像素冗余-对应表格自身的padding+border-防止出现超长字段
return Math.min(width + 2, 400);
}
export const focusInputAndChooseInput = (inputDom: HTMLInputElement) => {
const len = inputDom?.value?.length + 1;
if (!inputDom) return
inputDom.focus()
// inputDom.scrollIntoView({ behavior: 'smooth', block: 'center' })
functionInOneFrame(() => {
inputDom.setSelectionRange(0, len + 1)
})
}
/**
* Deep diff between two object, using lodash
* @param {Object} object Object compared
* @param {Object} base Object to compare with
* @return {Object} Return a new object who represent the diff
*/
export function difference<
T extends { [propName: string]: any },
U extends { [propName: string]: any }
>(object: T, base: U, keepProps?: Array<string>): { [propName: string]: any } {
function changes(object: T, base: U) {
if (isObject(object) && isObject(base)) {
const keys: Array<keyof T & keyof U> = uniq(
Object.keys(object).concat(Object.keys(base))
);
let result: any = {};
keys.forEach(key => {
const a: any = object[key as keyof T];
const b: any = base[key as keyof U];
if (keepProps && ~keepProps.indexOf(key as string)) {
result[key] = a;
}
if (isEqual(a, b)) {
return;
}
if (!object.hasOwnProperty(key)) {
result[key] = undefined;
} else if (Array.isArray(a) && Array.isArray(b)) {
result[key] = a;
} else {
result[key] = changes(a as any, b as any);
}
});
return result;
} else {
return object;
}
}
return changes(object, base);
}
export const padArr = (arr: Array<any>, size = 4): Array<Array<any>> => {
const ret: Array<Array<any>> = [];
const pool: Array<any> = arr.concat();
let from = 0;
while (pool.length) {
let host: Array<any> = ret[from] || (ret[from] = []);
if (host.length >= size) {
from += 1;
continue;
}
host.push(pool.shift());
}
return ret;
};
export function __uri(id: string) {
return id;
}
export function isObject(obj: any) {
const typename = typeof obj;
return (
obj &&
typename !== 'string' &&
typename !== 'number' &&
typename !== 'boolean' &&
typename !== 'function' &&
!Array.isArray(obj)
);
}
// xs < 768px
// sm >= 768px
// md >= 992px
// lg >= 1200px
export function isBreakpoint(str: string): boolean {
if (typeof str !== 'string') {
return !!str;
}
const breaks = str.split(/\s*,\s*|\s+/);
if ((window as any).matchMedia) {
return breaks.some(
item =>
item === '*' ||
(item === 'xs' &&
matchMedia(`screen and (max-width: 767px)`).matches) ||
(item === 'sm' &&
matchMedia(`screen and (min-width: 768px) and (max-width: 991px)`)
.matches) ||
(item === 'md' &&
matchMedia(`screen and (min-width: 992px) and (max-width: 1199px)`)
.matches) ||
(item === 'lg' && matchMedia(`screen and (min-width: 1200px)`).matches)
);
} else {
const width = window.innerWidth;
return breaks.some(
item =>
item === '*' ||
(item === 'xs' && width < 768) ||
(item === 'sm' && width >= 768 && width < 992) ||
(item === 'md' && width >= 992 && width < 1200) ||
(item === 'lg' && width >= 1200)
);
}
}
export function until(
fn: () => Promise<any>,
when: (ret: any) => boolean,
getCanceler: (fn: () => any) => void,
interval: number = 5000
) {
let timer: ReturnType<typeof setTimeout>;
let stoped: boolean = false;
return new Promise((resolve, reject) => {
let cancel = () => {
clearTimeout(timer);
stoped = true;
};
let check = async () => {
try {
const ret = await fn();
if (stoped) {
return;
} else if (when(ret)) {
stoped = true;
resolve(ret);
} else {
timer = setTimeout(check, interval);
}
} catch (e) {
reject(e);
}
};
check();
getCanceler && getCanceler(cancel);
});
}
export function omitControls(
controls: Array<any>,
omitItems: Array<string>
): Array<any> {
return controls.filter(
control => !~omitItems.indexOf(control.name || control._name)
);
}
export function isEmpty(thing: any) {
if (isObject(thing) && Object.keys(thing).length) {
return false;
}
return true;
}
/**
* 基于时间戳的 uuid
*
* @returns uniqueId
*/
export const uuid = () => {
return (+new Date()).toString(36);
};
// 参考 https://github.com/streamich/v4-uuid
const str = () =>
(
'00000000000000000' + (Math.random() * 0xffffffffffffffff).toString(16)
).slice(-16);
export const uuidv4 = () => {
const a = str();
const b = str();
return (
a.slice(0, 8) +
'-' +
a.slice(8, 12) +
'-4' +
a.slice(13) +
'-a' +
b.slice(1, 4) +
'-' +
b.slice(4)
);
};
export interface TreeItem {
children?: TreeArray;
[propName: string]: any;
}
export interface TreeArray extends Array<TreeItem> { }
/**
* 类似于 arr.map 方法,此方法主要针对类似下面示例的树形结构。
* [
* {
* children: []
* },
* // 其他成员
* ]
*
* @param {Tree} tree 树形数据
* @param {Function} iterator 处理函数,返回的数据会被替换成新的。
* @return {Tree} 返回处理过的 tree
*/
export function mapTree<T extends TreeItem>(
tree: Array<T>,
iterator: (item: T, key: number, level: number, paths: Array<T>) => T,
level: number = 1,
depthFirst: boolean = false,
paths: Array<T> = []
) {
return tree.map((item: any, index) => {
if (depthFirst) {
let children: TreeArray | undefined = item.children
? mapTree(
item.children,
iterator,
level + 1,
depthFirst,
paths.concat(item)
)
: undefined;
children && (item = { ...item, children: children });
item = iterator(item, index, level, paths) || { ...(item as object) };
return item;
}
item = iterator(item, index, level, paths) || { ...(item as object) };
if (item.children && item.children.splice) {
item.children = mapTree(
item.children,
iterator,
level + 1,
depthFirst,
paths.concat(item)
);
}
return item;
});
}
/**
* 遍历树
* @param tree
* @param iterator
*/
export function eachTree<T extends TreeItem>(
tree: Array<T>,
iterator: (item: T, key: number, level: number) => any,
level: number = 1
) {
tree.map((item, index) => {
iterator(item, index, level);
if (item.children) {
eachTree(item.children, iterator, level + 1);
}
});
}
/**
* 在树中查找节点。
* @param tree
* @param iterator
*/
export function findTree<T extends TreeItem>(
tree: Array<T>,
iterator: (item: T, key: number, level: number, paths: Array<T>) => any
): T | null {
let result: T | null = null;
everyTree(tree, (item, key, level, paths) => {
if (iterator(item, key, level, paths)) {
result = item;
return false;
}
return true;
});
return result;
}
/**
* 在树上查找节点,相同的逐一返回
* @param tree
* @param iterator
*/
export function findTreeList<T extends TreeItem>(
tree: Array<T>,
iterator: (item: T, key: number, level: number, paths: Array<T>) => any
): T[] | null {
let result: T[] = [];
everyTree(tree, (item, key, level, paths) => {
if (iterator(item, key, level, paths)) {
result.push(item);
}
if (item.children) {
findTreeList(item.children, iterator)
}
return true;
});
return result;
}
/**
* 在树中查找节点, 返回下标数组。
* @param tree
* @param iterator
*/
export function findTreeIndex<T extends TreeItem>(
tree: Array<T>,
iterator: (item: T, key: number, level: number, paths: Array<T>) => any
): Array<number> | undefined {
let idx: Array<number> = [];
findTree(tree, (item, index, level, paths) => {
if (iterator(item, index, level, paths)) {
idx = [index];
paths = paths.concat();
paths.unshift({
children: tree
} as any);
for (let i = paths.length - 1; i > 0; i--) {
const prev = paths[i - 1];
const current = paths[i];
idx.unshift(prev.children!.indexOf(current));
}
return true;
}
return false;
});
return idx.length ? idx : undefined;
}
export function getTree<T extends TreeItem>(
tree: Array<T>,
idx: Array<number> | number
): T | undefined | null {
const indexes = Array.isArray(idx) ? idx.concat() : [idx];
const lastIndex = indexes.pop()!;
let list: Array<T> | null = tree;
for (let i = 0, len = indexes.length; i < len; i++) {
const index = indexes[i];
if (!list![index]) {
list = null;
break;
}
list = list![index].children as any;
}
return list ? list[lastIndex] : undefined;
}
/**
* 过滤树节点
*
* @param tree
* @param iterator
*/
export function filterTree<T extends TreeItem>(
tree: Array<T>,
iterator: (item: T, key: number, level: number) => any,
level: number = 1,
depthFirst: boolean = false
) {
if (depthFirst) {
return tree
.map(item => {
let children: TreeArray | undefined = item.children
? filterTree(item.children, iterator, level + 1, depthFirst)
: undefined;
if (Array.isArray(children) && Array.isArray(item.children)) {
item = { ...item, children: children };
}
return item;
})
.filter((item, index) => iterator(item, index, level));
}
return tree
.filter((item, index) => iterator(item, index, level))
.map(item => {
if (item.children) {
let children = filterTree(
item.children,
iterator,
level + 1,
depthFirst
);
if (Array.isArray(children) && Array.isArray(item.children)) {
item = { ...item, children: children };
}
}
return item;
});
}
/**
* 判断树中每个节点是否满足某个条件。
* @param tree
* @param iterator
*/
export function everyTree<T extends TreeItem>(
tree: Array<T>,
iterator: (
item: T,
key: number,
level: number,
paths: Array<T>,
indexes: Array<number>
) => boolean,
level: number = 1,
paths: Array<T> = [],
indexes: Array<number> = []
): boolean {
return tree.every((item, index) => {
const value: any = iterator(item, index, level, paths, indexes);
if (value && item.children) {
return everyTree(
item.children,
iterator,
level + 1,
paths.concat(item),
indexes.concat(index)
);
}
return value;
});
}
/**
* 判断树中是否有某些节点满足某个条件。
* @param tree
* @param iterator
*/
export function someTree<T extends TreeItem>(
tree: Array<T>,
iterator: (item: T, key: number, level: number, paths: Array<T>) => boolean
): boolean {
let result = false;
everyTree(tree, (item: T, key: number, level: number, paths: Array<T>) => {
if (iterator(item, key, level, paths)) {
result = true;
return false;
}
return true;
});
return result;
}
/**
* 将树打平变成一维数组,可以传入第二个参数实现打平节点中的其他属性。
*
* 比如:
*
* flattenTree([
* {
* id: 1,
* children: [
* { id: 2 },
* { id: 3 },
* ]
* }
* ], item => item.id); // 输出位 [1, 2, 3]
*
* @param tree
* @param mapper
*/
export function flattenTree<T extends TreeItem>(tree: Array<T>): Array<T>;
export function flattenTree<T extends TreeItem, U>(
tree: Array<T>,
mapper: (value: T, index: number) => U
): Array<U>;
export function flattenTree<T extends TreeItem, U>(
tree: Array<T>,
mapper?: (value: T, index: number) => U
): Array<U> {
let flattened: Array<any> = [];
eachTree(tree, (item, index) =>
flattened.push(mapper ? mapper(item, index) : item)
);
return flattened;
}
/**
* 操作树,遵循 imutable, 每次返回一个新的树。
* 类似数组的 splice 不同的地方这个方法不修改原始数据,
* 同时第二个参数不是下标,而是下标数组,分别代表每一层的下标。
*
* 至于如何获取下标数组,请查看 findTreeIndex
*
* @param tree
* @param idx
* @param deleteCount
* @param ...items
*/
export function spliceTree<T extends TreeItem>(
tree: Array<T>,
idx: Array<number> | number,
deleteCount: number = 0,
...items: Array<T>
): Array<T> {
const list = tree.concat();
if (typeof idx === 'number') {
list.splice(idx, deleteCount, ...items);
} else if (Array.isArray(idx) && idx.length) {
idx = idx.concat();
const lastIdx = idx.pop()!;
let host = idx.reduce((list: Array<T>, idx) => {
const child = {
...list[idx],
children: list[idx].children ? list[idx].children!.concat() : []
};
list[idx] = child;
return child.children;
}, list);
host.splice(lastIdx, deleteCount, ...items);
}
return list;
}
/**
* 计算树的深度
* @param tree
*/
export function getTreeDepth<T extends TreeItem>(tree: Array<T>): number {
return Math.max(
...tree.map(item => {
if (Array.isArray(item.children)) {
return 1 + getTreeDepth(item.children);
}
return 1;
})
);
}
/**
* 从树中获取某个值的所有祖先
* @param tree
* @param value
*/
export function getTreeAncestors<T extends TreeItem>(
tree: Array<T>,
value: T,
includeSelf = false
): Array<T> | null {
let ancestors: Array<T> | null = null;
findTree(tree, (item, index, level, paths) => {
if (item === value) {
ancestors = paths;
if (includeSelf) {
ancestors.push(item);
}
return true;
}
return false;
});
return ancestors;
}
/**
* 从树中获取某个值的上级
* @param tree
* @param value
*/
export function getTreeParent<T extends TreeItem>(tree: Array<T>, value: T) {
const ancestors = getTreeAncestors(tree, value);
return ancestors?.length ? ancestors[ancestors.length - 1] : null;
}
export function ucFirst(str?: string) {
return typeof str === 'string'
? str.substring(0, 1).toUpperCase() + str.substring(1)
: str;
}
export function lcFirst(str?: string) {
return str ? str.substring(0, 1).toLowerCase() + str.substring(1) : '';
}
export function camel(str?: string) {
return str
? str
.split(/[\s_\-]/)
.map((item, index) => (index === 0 ? lcFirst(item) : ucFirst(item)))
.join('')
: '';
}
export function getWidthRate(value: any, strictMode = false): number {
if (typeof value === 'string' && /\bcol\-\w+\-(\d+)\b/.test(value)) {
return parseInt(RegExp.$1, 10);
}
return strictMode ? 0 : value || 0;
}
export function getLevelFromClassName(
value: string,
defaultValue: string = 'default'
) {
if (
/\b(?:btn|text)-(link|primary|secondary|info|success|warning|danger|light|dark)\b/.test(
value
)
) {
return RegExp.$1;
}
return defaultValue;
}
export function string2regExp(value: string, caseSensitive = false) {
if (typeof value !== 'string') {
throw new TypeError('Expected a string');
}
return new RegExp(
value.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d'),
!caseSensitive ? 'i' : ''
);
}
export function pickEventsProps(props: any) {
const ret: any = {};
props &&
Object.keys(props).forEach(
key => /^on/.test(key) && (ret[key] = props[key])
);
return ret;
}
export const autobind = autobindMethod;
export const bulkBindFunctions = function <
T extends {
[propName: string]: any;
}
>(context: T, funNames: Array<FunctionPropertyNames<T>>) {
funNames.forEach(key => (context[key] = context[key].bind(context)));
};
export function sortArray<T extends any>(
items: Array<T>,
field: string,
dir: -1 | 1
): Array<T> {
return items.sort((a: any, b: any) => {
let ret: number;
const a1 = a[field];
const b1 = b[field];
if (typeof a1 === 'number' && typeof b1 === 'number') {
ret = a1 < b1 ? -1 : a1 === b1 ? 0 : 1;
} else {
ret = String(a1).localeCompare(String(b1));
}
return ret * dir;
});
}
// 只判断一层, 如果层级很深,form-data 也不好表达。
export function hasFile(object: any): boolean {
return Object.keys(object).some(key => {
let value = object[key];
return (
value instanceof File ||
(Array.isArray(value) && value.length && value[0] instanceof File)
);
});
}
export function qsstringify(
data: any,
options: any = {
arrayFormat: 'indices',
encodeValuesOnly: true
},
keepEmptyArray?: boolean
) {
// qs会保留空字符串。fix: Combo模式的空数组,无法清空。改为存为空字符串;只转换一层
keepEmptyArray &&
Object.keys(data).forEach((key: any) => {
Array.isArray(data[key]) && !data[key].length && (data[key] = '');
});
return qs.stringify(data, options);
}
export function qsparse(
data: string,
options: any = {
arrayFormat: 'indices',
encodeValuesOnly: true,
depth: 1000 // 默认是 5, 所以condition-builder只要来个条件组就会导致报错
}
) {
return qs.parse(data, options);
}
export function object2formData(
data: any,
options: any = {
arrayFormat: 'indices',
encodeValuesOnly: true
},
fd: FormData = new FormData()
): any {
let fileObjects: any = [];
let others: any = {};
Object.keys(data).forEach(key => {
const value = data[key];
if (value instanceof File) {
fileObjects.push([key, value]);
} else if (
Array.isArray(value) &&
value.length &&
value[0] instanceof File
) {
value.forEach(value => fileObjects.push([`${key}[]`, value]));
} else {
others[key] = value;
}
});
// 因为 key 的格式太多了,偷个懒,用 qs 来处理吧。
qsstringify(others, options)
.split('&')
.forEach(item => {
let parts = item.split('=');
// form-data/multipart 是不需要 encode 值的。
parts[0] && fd.append(parts[0], decodeURIComponent(parts[1]));
});
// Note: File类型字段放在后面,可以支持第三方云存储鉴权
fileObjects.forEach((fileObject: any[]) =>
fd.append(fileObject[0], fileObject[1], fileObject[1].name)
);
return fd;
}
export function chainFunctions(
...fns: Array<(...args: Array<any>) => void>
): (...args: Array<any>) => void {
return (...args: Array<any>) =>
fns.reduce(
(ret: any, fn: any) =>
ret === false
? false
: typeof fn == 'function'
? fn(...args)
: undefined,
undefined
);
}
export function chainEvents(props: any, schema: any) {
const ret: any = {};
Object.keys(props).forEach(key => {
if (
key.substr(0, 2) === 'on' &&
typeof props[key] === 'function' &&
typeof schema[key] === 'function' &&
schema[key] !== props[key]
) {
// 表单项里面的 onChange 很特殊,这个不要处理。
if (props.formStore && key === 'onChange') {
ret[key] = props[key];
} else {
ret[key] = chainFunctions(schema[key], props[key]);
}
} else {
ret[key] = props[key];
}
});
return ret;
}
export function mapObject(value: any, fn: Function): any {
if (Array.isArray(value)) {
return value.map(item => mapObject(item, fn));
}
if (isObject(value)) {
let tmpValue = { ...value };
Object.keys(tmpValue).forEach(key => {
(tmpValue as PlainObject)[key] = mapObject(
(tmpValue as PlainObject)[key],
fn
);
});
return tmpValue;
}
return fn(value);
}
export function loadScript(src: string) {
return new Promise<void>((ok, fail) => {
const script = document.createElement('script');
script.onerror = reason => fail(reason);
if (~src.indexOf('{{callback}}')) {
const callbackFn = `loadscriptcallback_${uuid()}`;
(window as any)[callbackFn] = () => {
ok();
delete (window as any)[callbackFn];
};
src = src.replace('{{callback}}', callbackFn);
} else {
script.onload = () => ok();
}
script.src = src;
document.head.appendChild(script);
});
}
export class SkipOperation extends Error { }
/**
* 将例如像 a.b.c 或 a[1].b 的字符串转换为路径数组
*
* @param string 要转换的字符串
*/
export const keyToPath = (string: string) => {
const result = [];
if (string.charCodeAt(0) === '.'.charCodeAt(0)) {
result.push('');
}
string.replace(
new RegExp(
'[^.[\\]]+|\\[(?:([^"\'][^[]*)|(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))',
'g'
),
(match, expression, quote, subString) => {
let key = match;
if (quote) {
key = subString.replace(/\\(\\)?/g, '$1');
} else if (expression) {
key = expression.trim();
}
result.push(key);
return '';
}
);
return result;
};
/**
* 检查对象是否有循环引用,来自 https://stackoverflow.com/a/34909127
* @param obj
*/
function isCyclic(obj: any): boolean {
const seenObjects: any = [];
function detect(obj: any) {
if (obj && typeof obj === 'object') {
if (seenObjects.indexOf(obj) !== -1) {
return true;
}
seenObjects.push(obj);
for (var key in obj) {
if (obj.hasOwnProperty(key) && detect(obj[key])) {
return true;
}
}
}
return false;
}
return detect(obj);
}
function internalFindObjectsWithKey(obj: any, key: string) {
let objects: any[] = [];
for (const k in obj) {
if (!obj.hasOwnProperty(k)) continue;
if (k === key) {
objects.push(obj);
} else if (typeof obj[k] === 'object') {
objects = objects.concat(internalFindObjectsWithKey(obj[k], key));
}
}
return objects;
}
/**
* 深度查找具有某个 key 名字段的对象,实际实现是 internalFindObjectsWithKey,这里包一层是为了做循环引用检测
* @param obj
* @param key
*/
export function findObjectsWithKey(obj: any, key: string) {
// 避免循环引用导致死循环
if (isCyclic(obj)) {
return [];
}
return internalFindObjectsWithKey(obj, key);
}
let scrollbarWidth: number;
/**
* 获取浏览器滚动条宽度 https://stackoverflow.com/a/13382873
*/
export function getScrollbarWidth() {
if (typeof scrollbarWidth !== 'undefined') {
return scrollbarWidth;
}
// Creating invisible container
const outer = document.createElement('div');
outer.style.visibility = 'hidden';
outer.style.overflow = 'scroll'; // forcing scrollbar to appear
// @ts-ignore
outer.style.msOverflowStyle = 'scrollbar'; // needed for WinJS apps
document.body.appendChild(outer);
// Creating inner element and placing it in the container
const inner = document.createElement('div');
outer.appendChild(inner);
// Calculating difference between container's full width and the child width
scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
// Removing temporary elements from the DOM
// @ts-ignore
outer.parentNode.removeChild(outer);
return scrollbarWidth;
}
function resolveValueByName(data: any, name?: string) {
return isPureVariable(name)
? resolveVariableAndFilter(name, data)
: resolveVariable(name, data);
}
// 统一的获取 value 值方法
export function getPropValue<
T extends {
value?: any;
name?: string;
data?: any;
defaultValue?: any;
}
>(props: T, getter?: (props: T) => any) {
const { name, value, data, defaultValue } = props;
return (
value ?? getter?.(props) ?? resolveValueByName(data, name) ?? defaultValue
);
}
// 检测 value 是否有变化,有变化就执行 onChange
export function detectPropValueChanged<
T extends {
value?: any;
name?: string;
data?: any;
defaultValue?: any;
}
>(
props: T,
prevProps: T,
onChange: (value: any) => void,
getter?: (props: T) => any
) {
let nextValue: any;
if (typeof props.value !== 'undefined') {
props.value !== prevProps.value && onChange(props.value);
} else if ((nextValue = getter?.(props)) !== undefined) {
nextValue !== getter!(prevProps) && onChange(nextValue);
} else if (
typeof props.name === 'string' &&
(nextValue = resolveValueByName(props.data, props.name)) !== undefined
) {
nextValue !== resolveValueByName(prevProps.data, prevProps.name) &&
onChange(nextValue);
} else if (props.defaultValue !== prevProps.defaultValue) {
onChange(props.defaultValue);
}
}
// 去掉字符串中的 html 标签,不完全准确但效率比较高
export function removeHTMLTag(str: string) {
return typeof str === 'string' ? str.replace(/<\/?[^>]+(>|$)/g, '') : str;
}
/**
* 将路径格式的value转换成普通格式的value值
*
* @example
*
* 'a/b/c' => 'c';
* {label: 'A/B/C', value: 'a/b/c'} => {label: 'C', value: 'c'};
* 'a/b/c,a/d' => 'c,d';
* ['a/b/c', 'a/d'] => ['c', 'd'];
* [{label: 'A/B/C', value: 'a/b/c'},{label: 'A/D', value: 'a/d'}] => [{label: 'C', value: 'c'},{label: 'D', value: 'd'}]
*/
export function normalizeNodePath(
value: any,
enableNodePath: boolean,
labelField: string = 'label',
valueField: string = 'value',
pathSeparator: string = '/',
delimiter: string = ','
) {
const nodeValueArray: any[] = [];
const nodePathArray: any[] = [];
const getLastNodeFromPath = (path: any) =>
last(path ? path.toString().split(pathSeparator) : []);
if (typeof value === 'undefined' || !enableNodePath) {
return { nodeValueArray, nodePathArray };
}
// 尾节点为当前options中value值
if (Array.isArray(value)) {
value.forEach(nodePath => {
if (nodePath && nodePath.hasOwnProperty(valueField)) {
nodeValueArray.push({
...nodePath,
[labelField]: getLastNodeFromPath(nodePath[labelField]),
[valueField]: getLastNodeFromPath(nodePath[valueField])
});
nodePathArray.push(nodePath[valueField]);
} else {
nodeValueArray.push(getLastNodeFromPath(nodePath));
nodePathArray.push(nodePath);
}
});
} else if (typeof value === 'string') {
value
.toString()
.split(delimiter)
.forEach(path => {
nodeValueArray.push(getLastNodeFromPath(path));
nodePathArray.push(path);
});
} else {
nodeValueArray.push({
...value,
[labelField]: getLastNodeFromPath(value[labelField]),
[valueField || 'value']: getLastNodeFromPath(value[valueField])
});
nodePathArray.push(value[valueField]);
}
return { nodeValueArray, nodePathArray };
}
// 主要用于排除点击输入框和链接等情况
export function isClickOnInput(e: React.MouseEvent<HTMLElement>) {
const target: HTMLElement = e.target as HTMLElement;
let formItem;
if (
!e.currentTarget.contains(target) ||
~['INPUT', 'TEXTAREA'].indexOf(target.tagName) ||
((formItem = target.closest(`button, a, [data-role="form-item"]`)) &&
e.currentTarget.contains(formItem))
) {
return true;
}
return false;
}
// 计算字符串 hash
export function hashCode(s: string): number {
return s.split('').reduce((a, b) => {
a = (a << 5) - a + b.charCodeAt(0);
return a & a;
}, 0);
}
/**
* 遍历 schema
* @param json
* @param mapper
*/
export function JSONTraverse(
json: any,
mapper: (value: any, key: string | number, host: Object) => any
) {
Object.keys(json).forEach(key => {
const value: any = json[key];
if (isPlainObject(value) || Array.isArray(value)) {
JSONTraverse(value, mapper);
} else {
mapper(value, key, json);
}
});
}
export function convertArrayValueToMoment(
value: number[],
types: string[],
mom: moment.Moment
): moment.Moment {
if (value.length === 0) return mom;
for (let i = 0; i < types.length; i++) {
const type = types[i];
// @ts-ignore
mom.set(type, value[i]);
}
return mom;
}
export function getRange(min: number, max: number, step: number = 1) {
const arr = [];
for (let i = min; i <= max; i += step) {
arr.push(i);
}
return arr;
}
// 数组去重
export function uniqueArr(arr: any[]) {
let newArr: any[] = [];
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i]);
}
}
return newArr;
}
export function handleActionCallback_10003(
data: any,
store: any, // store
handleAction?: any,
closeDialog?: Function, // 关闭某页面打开的dialog
returnValue?: boolean,
env?: RendererEnv
) {
const is_specialStaus = data?.hasOwnProperty('status');
let schemaBody;
if (is_specialStaus) {
schemaBody = {
"title": "提示",
"type": "dialog",
"body": data?.showText,
"actions": [
{
"actionType": "cancel",
"label": "取消",
"type": "action"
}, {
"close": true,
"label": "确认",
"type": "action",
"api": data?.nextAddr,
"actionType": "ajax",
"onAction": handleAction,
"level": "danger"
}
]
};
store.setCurrentAction({
type: 'button',
actionTyp