mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
feat: CRUD & CRUD2 parsePrimitiveQuery配置支持转化数字类型
This commit is contained in:
parent
2c05f9c7fb
commit
6be9dcfda8
@ -204,14 +204,28 @@ CRUD 组件对数据源接口的数据结构要求如下:
|
|||||||
|
|
||||||
> `3.5.0`及以上版本
|
> `3.5.0`及以上版本
|
||||||
|
|
||||||
`syncLocation`开启后,CRUD 在初始化数据域时,将会对 url 中的 Query 进行转换,将原始类型的字符串格式的转化为同位类型,目前仅支持**布尔类型**
|
`syncLocation`开启后,CRUD 在初始化数据域时,将会对 url 中的 Query 进行转换,将原始类型的字符串格式的转化为同位类型。`3.6.0`版本后支持对象格式,该配置默认开启,且默认仅转化布尔值。
|
||||||
|
|
||||||
```
|
#### ParsePrimitiveQueryOptions
|
||||||
"true" ==> true
|
```typescript
|
||||||
"false" ==> false
|
interface ParsePrimitiveQueryOptions {
|
||||||
|
parsePrimitiveQuery: {
|
||||||
|
enable: boolean;
|
||||||
|
types?: ('boolean' | 'number')[]
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
如果只想保持字符串格式,可以设置`"parsePrimitiveQuery": false`关闭该特性,具体效果参考[示例](../../../examples/crud/parse-primitive-query)。
|
比如开启设置 `{"parsePrimitiveQuery": {"enable": true, "types": ["boolean", "number"]}}` 后:
|
||||||
|
|
||||||
|
```
|
||||||
|
"true" ==> true
|
||||||
|
"false" ==> false
|
||||||
|
"123" ==> 123
|
||||||
|
"123.4" ==> 123.4
|
||||||
|
```
|
||||||
|
|
||||||
|
如果只想保持字符串格式,可以设置`"parsePrimitiveQuery": false` 或者 `"parsePrimitiveQuery": {"enable": false}` 关闭该特性,具体效果参考[示例](../../../examples/crud/parse-primitive-query)。如果想实现字段定制化转化类型,可以使用[配置API请求数据](../../docs/types/api#配置请求数据),通过表达式控制接口传递的参数类型。
|
||||||
|
|
||||||
## 功能
|
## 功能
|
||||||
|
|
||||||
@ -3381,6 +3395,8 @@ itemAction 里的 onClick 还能通过 `data` 参数拿到当前行的数据,
|
|||||||
| autoFillHeight | `boolean` 丨 `{height: number}` | | 内容区域自适应高度 |
|
| autoFillHeight | `boolean` 丨 `{height: number}` | | 内容区域自适应高度 |
|
||||||
| canAccessSuperData | `boolean` | `true` | 指定是否可以自动获取上层的数据并映射到表格行数据上,如果列也配置了该属性,则列的优先级更高 |
|
| canAccessSuperData | `boolean` | `true` | 指定是否可以自动获取上层的数据并映射到表格行数据上,如果列也配置了该属性,则列的优先级更高 |
|
||||||
| matchFunc | `string` | [`CRUDMatchFunc`](#匹配函数) | 自定义匹配函数, 当开启`loadDataOnce`时,会基于该函数计算的匹配结果进行过滤,主要用于处理列字段类型较为复杂或者字段值格式和后端返回不一致的场景 | `3.5.0` |
|
| matchFunc | `string` | [`CRUDMatchFunc`](#匹配函数) | 自定义匹配函数, 当开启`loadDataOnce`时,会基于该函数计算的匹配结果进行过滤,主要用于处理列字段类型较为复杂或者字段值格式和后端返回不一致的场景 | `3.5.0` |
|
||||||
|
| parsePrimitiveQuery | [`ParsePrimitiveQueryOptions`](#ParsePrimitiveQueryOptions) | `true` | 是否开启Query信息转换,开启后将会对url中的Query进行转换,默认开启,默认仅转化布尔值 | `3.6.0` |
|
||||||
|
|
||||||
|
|
||||||
注意除了上面这些属性,CRUD 在不同模式下的属性需要参考各自的文档,比如
|
注意除了上面这些属性,CRUD 在不同模式下的属性需要参考各自的文档,比如
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ export default {
|
|||||||
"type": "alert",
|
"type": "alert",
|
||||||
"body": {
|
"body": {
|
||||||
"type": "html",
|
"type": "html",
|
||||||
"html": "<code>parsePrimitiveQuery</code>默认开启,开启后会对url中的Query进行转换,将原始类型的字符串格式的转化为同位类型,目前仅支持<strong>布尔类型</strong>"
|
"html": "<code>parsePrimitiveQuery</code>默认开启,开启后会对url中的Query进行转换,将原始类型的字符串格式的转化为同位类型"
|
||||||
},
|
},
|
||||||
"level": "info",
|
"level": "info",
|
||||||
"showCloseButton": true,
|
"showCloseButton": true,
|
||||||
@ -17,6 +17,10 @@ export default {
|
|||||||
"name": "crud",
|
"name": "crud",
|
||||||
"syncLocation": true,
|
"syncLocation": true,
|
||||||
"api": "/api/mock2/crud/table5",
|
"api": "/api/mock2/crud/table5",
|
||||||
|
"parsePrimitiveQuery": {
|
||||||
|
"enable": true,
|
||||||
|
"types": ["boolean", "number"]
|
||||||
|
},
|
||||||
"filter": {
|
"filter": {
|
||||||
"debug": true,
|
"debug": true,
|
||||||
"title": "条件搜索",
|
"title": "条件搜索",
|
||||||
@ -30,6 +34,16 @@ export default {
|
|||||||
"label": "已核验",
|
"label": "已核验",
|
||||||
"size": "sm",
|
"size": "sm",
|
||||||
"value": false
|
"value": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "select",
|
||||||
|
"name": "version",
|
||||||
|
"label": "版本",
|
||||||
|
"options": [
|
||||||
|
{"label": "5.5", value: 5.5},
|
||||||
|
{"label": "6", value: 6},
|
||||||
|
{"label": "7", value: 7}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -67,6 +81,10 @@ export default {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "version",
|
||||||
|
"label": "版本"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -2050,21 +2050,52 @@ export function isNumeric(value: any): boolean {
|
|||||||
return /^[-+]?(?:\d*[.])?\d+$/.test(value);
|
return /^[-+]?(?:\d*[.])?\d+$/.test(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type PrimitiveTypes = 'boolean' | 'number';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析Query字符串中的原始类型,目前仅支持转化布尔类型
|
* 解析Query字符串中的原始类型,目前仅支持转化布尔类型
|
||||||
*
|
*
|
||||||
* @param query 查询字符串
|
* @param query 查询字符串
|
||||||
|
* @param options 配置参数
|
||||||
* @returns 解析后的查询字符串
|
* @returns 解析后的查询字符串
|
||||||
*/
|
*/
|
||||||
export function parsePrimitiveQueryString(rawQuery: Record<string, any>) {
|
export function parsePrimitiveQueryString(
|
||||||
|
rawQuery: Record<string, any>,
|
||||||
|
options?: {
|
||||||
|
primitiveTypes: PrimitiveTypes[];
|
||||||
|
}
|
||||||
|
) {
|
||||||
if (!isPlainObject(rawQuery)) {
|
if (!isPlainObject(rawQuery)) {
|
||||||
return rawQuery;
|
return rawQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options = options || {primitiveTypes: ['boolean']};
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Array.isArray(options.primitiveTypes) ||
|
||||||
|
options.primitiveTypes.length === 0
|
||||||
|
) {
|
||||||
|
options.primitiveTypes = ['boolean'];
|
||||||
|
}
|
||||||
|
|
||||||
const query = JSONValueMap(rawQuery, value => {
|
const query = JSONValueMap(rawQuery, value => {
|
||||||
/** 解析布尔类型,后续有需要在这里扩充 */
|
if (
|
||||||
if (value === 'true' || value === 'false') {
|
(options?.primitiveTypes?.includes('boolean') && value === 'true') ||
|
||||||
|
value === 'false'
|
||||||
|
) {
|
||||||
|
/** 解析布尔类型 */
|
||||||
return value === 'true';
|
return value === 'true';
|
||||||
|
} else if (
|
||||||
|
options?.primitiveTypes?.includes('number') &&
|
||||||
|
isNumeric(value) &&
|
||||||
|
isFinite(value) &&
|
||||||
|
value >= -Number.MAX_SAFE_INTEGER &&
|
||||||
|
value <= Number.MAX_SAFE_INTEGER
|
||||||
|
) {
|
||||||
|
/** 解析数字类型 */
|
||||||
|
const result = Number(value);
|
||||||
|
|
||||||
|
return !isNaN(result) ? result : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
@ -2082,16 +2113,19 @@ export function parsePrimitiveQueryString(rawQuery: Record<string, any>) {
|
|||||||
*/
|
*/
|
||||||
export function parseQuery(
|
export function parseQuery(
|
||||||
location?: Location | {query?: any; search?: any; [propName: string]: any},
|
location?: Location | {query?: any; search?: any; [propName: string]: any},
|
||||||
options?: {parsePrimitive?: boolean}
|
options?: {
|
||||||
|
parsePrimitive?: boolean;
|
||||||
|
primitiveTypes?: PrimitiveTypes[];
|
||||||
|
}
|
||||||
): Record<string, any> {
|
): Record<string, any> {
|
||||||
const {parsePrimitive = false} = options || {};
|
const {parsePrimitive = false, primitiveTypes = ['boolean']} = options || {};
|
||||||
const query =
|
const query =
|
||||||
(location && !(location instanceof Location) && location?.query) ||
|
(location && !(location instanceof Location) && location?.query) ||
|
||||||
(location && location?.search && qsparse(location.search.substring(1))) ||
|
(location && location?.search && qsparse(location.search.substring(1))) ||
|
||||||
(window.location.search && qsparse(window.location.search.substring(1)));
|
(window.location.search && qsparse(window.location.search.substring(1)));
|
||||||
const normalizedQuery = isPlainObject(query)
|
const normalizedQuery = isPlainObject(query)
|
||||||
? parsePrimitive
|
? parsePrimitive
|
||||||
? parsePrimitiveQueryString(query)
|
? parsePrimitiveQueryString(query, {primitiveTypes})
|
||||||
: query
|
: query
|
||||||
: {};
|
: {};
|
||||||
/* 处理hash中的query */
|
/* 处理hash中的query */
|
||||||
|
@ -26,7 +26,7 @@ import {
|
|||||||
import {ScopedContext, IScopedContext} from 'amis-core';
|
import {ScopedContext, IScopedContext} from 'amis-core';
|
||||||
import {Button, SpinnerExtraProps, TooltipWrapper} from 'amis-ui';
|
import {Button, SpinnerExtraProps, TooltipWrapper} from 'amis-ui';
|
||||||
import {Select} from 'amis-ui';
|
import {Select} from 'amis-ui';
|
||||||
import {getExprProperties} from 'amis-core';
|
import {getExprProperties, isObject} from 'amis-core';
|
||||||
import pick from 'lodash/pick';
|
import pick from 'lodash/pick';
|
||||||
import {findDOMNode} from 'react-dom';
|
import {findDOMNode} from 'react-dom';
|
||||||
import {evalExpression, filter} from 'amis-core';
|
import {evalExpression, filter} from 'amis-core';
|
||||||
@ -376,9 +376,14 @@ export interface CRUDCommonSchema extends BaseSchema, SpinnerExtraProps {
|
|||||||
autoFillHeight?: TableSchema['autoFillHeight'];
|
autoFillHeight?: TableSchema['autoFillHeight'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否开启Query信息转换,开启后将会对url中的Query进行转换,将字符串格式的布尔值转化为同位类型
|
* 是否开启Query信息转换,开启后将会对url中的Query进行转换,默认开启,默认仅转化布尔值
|
||||||
*/
|
*/
|
||||||
parsePrimitiveQuery?: boolean;
|
parsePrimitiveQuery?:
|
||||||
|
| {
|
||||||
|
enable: boolean;
|
||||||
|
types?: ('boolean' | 'number')[];
|
||||||
|
}
|
||||||
|
| boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CRUDCardsSchema = CRUDCommonSchema & {
|
export type CRUDCardsSchema = CRUDCommonSchema & {
|
||||||
@ -545,22 +550,22 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
|||||||
pageField,
|
pageField,
|
||||||
perPageField,
|
perPageField,
|
||||||
syncLocation,
|
syncLocation,
|
||||||
loadDataOnce,
|
loadDataOnce
|
||||||
parsePrimitiveQuery
|
|
||||||
} = props;
|
} = props;
|
||||||
|
const parseQueryOptions = this.getParseQueryOptions(props);
|
||||||
|
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
|
|
||||||
if (syncLocation && location && (location.query || location.search)) {
|
if (syncLocation && location && (location.query || location.search)) {
|
||||||
store.updateQuery(
|
store.updateQuery(
|
||||||
parseQuery(location, {parsePrimitive: parsePrimitiveQuery}),
|
parseQuery(location, parseQueryOptions),
|
||||||
undefined,
|
undefined,
|
||||||
pageField,
|
pageField,
|
||||||
perPageField
|
perPageField
|
||||||
);
|
);
|
||||||
} else if (syncLocation && !location && window.location.search) {
|
} else if (syncLocation && !location && window.location.search) {
|
||||||
store.updateQuery(
|
store.updateQuery(
|
||||||
parseQuery(window.location, {parsePrimitive: parsePrimitiveQuery}),
|
parseQuery(window.location, parseQueryOptions),
|
||||||
undefined,
|
undefined,
|
||||||
pageField,
|
pageField,
|
||||||
perPageField
|
perPageField
|
||||||
@ -654,7 +659,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
|||||||
) {
|
) {
|
||||||
// 同步地址栏,那么直接检测 query 是否变了,变了就重新拉数据
|
// 同步地址栏,那么直接检测 query 是否变了,变了就重新拉数据
|
||||||
store.updateQuery(
|
store.updateQuery(
|
||||||
parseQuery(props.location, {parsePrimitive: props.parsePrimitiveQuery}),
|
parseQuery(props.location, this.getParseQueryOptions(props)),
|
||||||
undefined,
|
undefined,
|
||||||
props.pageField,
|
props.pageField,
|
||||||
props.perPageField
|
props.perPageField
|
||||||
@ -705,6 +710,22 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
|||||||
this.filterOnEvent.cache.clear?.();
|
this.filterOnEvent.cache.clear?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getParseQueryOptions(props: CRUDProps) {
|
||||||
|
const {parsePrimitiveQuery} = props;
|
||||||
|
type PrimitiveQueryObj = Exclude<CRUDProps['parsePrimitiveQuery'], boolean>;
|
||||||
|
|
||||||
|
const normalizedOptions = {
|
||||||
|
parsePrimitive: !!(isObject(parsePrimitiveQuery)
|
||||||
|
? (parsePrimitiveQuery as PrimitiveQueryObj)?.enable
|
||||||
|
: parsePrimitiveQuery),
|
||||||
|
primitiveTypes: (parsePrimitiveQuery as PrimitiveQueryObj)?.types ?? [
|
||||||
|
'boolean'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
return normalizedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
/** 查找CRUD最近层级的父窗口 */
|
/** 查找CRUD最近层级的父窗口 */
|
||||||
getClosestParentContainer() {
|
getClosestParentContainer() {
|
||||||
const dom = findDOMNode(this) as HTMLElement;
|
const dom = findDOMNode(this) as HTMLElement;
|
||||||
@ -996,6 +1017,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
|||||||
loadDataOnceFetchOnFilter,
|
loadDataOnceFetchOnFilter,
|
||||||
parsePrimitiveQuery
|
parsePrimitiveQuery
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
const parseQueryOptions = this.getParseQueryOptions(this.props);
|
||||||
|
|
||||||
/** 找出clearValueOnHidden的字段, 保证updateQuery时不会使用上次的保留值 */
|
/** 找出clearValueOnHidden的字段, 保证updateQuery时不会使用上次的保留值 */
|
||||||
values = {
|
values = {
|
||||||
@ -1008,7 +1030,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
|||||||
|
|
||||||
/** 把布尔值反解出来 */
|
/** 把布尔值反解出来 */
|
||||||
if (parsePrimitiveQuery) {
|
if (parsePrimitiveQuery) {
|
||||||
values = parsePrimitiveQueryString(values);
|
values = parsePrimitiveQueryString(values, parseQueryOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
store.updateQuery(
|
store.updateQuery(
|
||||||
|
@ -196,9 +196,14 @@ export interface CRUD2CommonSchema extends BaseSchema, SpinnerExtraProps {
|
|||||||
primaryField?: string;
|
primaryField?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否开启Query信息转换,开启后将会对url中的Query进行转换,将字符串格式的布尔值转化为同位类型
|
* 是否开启Query信息转换,开启后将会对url中的Query进行转换,默认开启,默认仅转化布尔值
|
||||||
*/
|
*/
|
||||||
parsePrimitiveQuery?: boolean;
|
parsePrimitiveQuery?:
|
||||||
|
| {
|
||||||
|
enable: boolean;
|
||||||
|
types?: ('boolean' | 'number')[];
|
||||||
|
}
|
||||||
|
| boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CRUD2CardsSchema = CRUD2CommonSchema & {
|
export type CRUD2CardsSchema = CRUD2CommonSchema & {
|
||||||
@ -305,19 +310,20 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
|
|||||||
perPageField,
|
perPageField,
|
||||||
parsePrimitiveQuery
|
parsePrimitiveQuery
|
||||||
} = props;
|
} = props;
|
||||||
|
const parseQueryOptions = this.getParseQueryOptions(props);
|
||||||
|
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
|
|
||||||
if (syncLocation && location && (location.query || location.search)) {
|
if (syncLocation && location && (location.query || location.search)) {
|
||||||
store.updateQuery(
|
store.updateQuery(
|
||||||
parseQuery(location, {parsePrimitive: parsePrimitiveQuery}),
|
parseQuery(location, parseQueryOptions),
|
||||||
undefined,
|
undefined,
|
||||||
pageField,
|
pageField,
|
||||||
perPageField
|
perPageField
|
||||||
);
|
);
|
||||||
} else if (syncLocation && !location && window.location.search) {
|
} else if (syncLocation && !location && window.location.search) {
|
||||||
store.updateQuery(
|
store.updateQuery(
|
||||||
parseQuery(window.location, {parsePrimitive: parsePrimitiveQuery}),
|
parseQuery(window.location, parseQueryOptions),
|
||||||
undefined,
|
undefined,
|
||||||
pageField,
|
pageField,
|
||||||
perPageField
|
perPageField
|
||||||
@ -389,7 +395,7 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
|
|||||||
) {
|
) {
|
||||||
// 同步地址栏,那么直接检测 query 是否变了,变了就重新拉数据
|
// 同步地址栏,那么直接检测 query 是否变了,变了就重新拉数据
|
||||||
store.updateQuery(
|
store.updateQuery(
|
||||||
parseQuery(props.location, {parsePrimitive: parsePrimitiveQuery}),
|
parseQuery(props.location, this.getParseQueryOptions(props)),
|
||||||
undefined,
|
undefined,
|
||||||
props.pageField,
|
props.pageField,
|
||||||
props.perPageField
|
props.perPageField
|
||||||
@ -439,6 +445,25 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
|
|||||||
clearTimeout(this.timer);
|
clearTimeout(this.timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getParseQueryOptions(props: CRUD2Props) {
|
||||||
|
const {parsePrimitiveQuery} = props;
|
||||||
|
type PrimitiveQueryObj = Exclude<
|
||||||
|
CRUD2Props['parsePrimitiveQuery'],
|
||||||
|
boolean
|
||||||
|
>;
|
||||||
|
|
||||||
|
const normalizedOptions = {
|
||||||
|
parsePrimitive: !!(isObject(parsePrimitiveQuery)
|
||||||
|
? (parsePrimitiveQuery as PrimitiveQueryObj)?.enable
|
||||||
|
: parsePrimitiveQuery),
|
||||||
|
primitiveTypes: (parsePrimitiveQuery as PrimitiveQueryObj)?.types ?? [
|
||||||
|
'boolean'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
return normalizedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
controlRef(control: any) {
|
controlRef(control: any) {
|
||||||
// 因为 control 有可能被 n 层 hoc 包裹。
|
// 因为 control 有可能被 n 层 hoc 包裹。
|
||||||
@ -502,6 +527,7 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
|
|||||||
perPageField,
|
perPageField,
|
||||||
parsePrimitiveQuery
|
parsePrimitiveQuery
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
const parseQueryOptions = this.getParseQueryOptions(this.props);
|
||||||
let {query, resetQuery, replaceQuery, loadMore, resetPage} = data || {};
|
let {query, resetQuery, replaceQuery, loadMore, resetPage} = data || {};
|
||||||
|
|
||||||
query =
|
query =
|
||||||
@ -511,7 +537,7 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
|
|||||||
|
|
||||||
/** 把布尔值反解出来 */
|
/** 把布尔值反解出来 */
|
||||||
if (parsePrimitiveQuery) {
|
if (parsePrimitiveQuery) {
|
||||||
query = parsePrimitiveQueryString(query);
|
query = parsePrimitiveQueryString(query, parseQueryOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
store.updateQuery(
|
store.updateQuery(
|
||||||
|
Loading…
Reference in New Issue
Block a user