feat: CRUD支持matchFunc,用于前端分页的搜索匹配函数 (#8556)

* feat: CRUD支持matchFunc,用于前端分页的搜索匹配函数

* patch(feat: CRUD支持matchFunc): 搜索函数执行时机修改,支持source为上下文数据
This commit is contained in:
RUNZE LU 2023-10-31 16:42:15 +08:00 committed by GitHub
parent 7380d4b09e
commit b3256870c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 1021 additions and 76 deletions

View File

@ -3085,6 +3085,33 @@ CRUD 中不限制有多少个单条操作、添加一个操作对应的添加一
}
```
### 匹配函数
> `3.5.0` 及以上版本
支持自定义匹配函数`matchFunc`,当开启`loadDataOnce`时,会基于该函数计算的匹配结果进行过滤,主要用于处理列字段类型较为复杂或者字段值格式和后端返回不一致的场景,函数签名如下:
```typescript
interface CRUDMatchFunc {
(
/* 当前列表的全量数据 */
items: any,
/* 最近一次接口返回的全量数据 */
itemsRaw: any,
/** 相关配置 */
options?: {
/* 查询参数 */
query: Record<string, any>,
/* 列配置 */
columns: any;
}
): boolean;
}
```
具体效果请参考[示例](../../../examples/crud/match-func)。
## 动态列
> since 1.1.6
@ -3220,8 +3247,8 @@ itemAction 里的 onClick 还能通过 `data` 参数拿到当前行的数据,
## 属性表
| 属性名 | 类型 | 默认值 | 说明 |
| ------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| 属性名 | 类型 | 默认值 | 说明 | 版本 |
| ------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------- | --- |
| type | `string` | | `type` 指定为 CRUD 渲染器 |
| mode | `string` | `"table"` | `"table" 、 "cards" 或者 "list"` |
| title | `string` | `""` | 可设置成空,当设置成空时,没有标题栏 |
@ -3276,6 +3303,8 @@ itemAction 里的 onClick 还能通过 `data` 参数拿到当前行的数据,
| resetPageAfterAjaxItemAction | `boolean` | `false` | 单条数据 ajax 操作后是否重置页码为第一页 |
| autoFillHeight | `boolean``{height: number}` | | 内容区域自适应高度 |
| canAccessSuperData | `boolean` | `true` | 指定是否可以自动获取上层的数据并映射到表格行数据上,如果列也配置了该属性,则列的优先级更高 |
| matchFunc | `string` | [`CRUDMatchFunc`](#匹配函数) | 自定义匹配函数, 当开启`loadDataOnce`时,会基于该函数计算的匹配结果进行过滤,主要用于处理列字段类型较为复杂或者字段值格式和后端返回不一致的场景 | `3.5.0` |
注意除了上面这些属性CRUD 在不同模式下的属性需要参考各自的文档,比如

View File

@ -0,0 +1,298 @@
export default {
type: 'page',
title: '匹配函数',
remark: '使用前端分页处理列字段类型较为复杂或者字段值格式和后端返回不一致的场景',
body: [
{
type: 'container',
style: {
padding: '8px',
marginBottom: '8px',
backgroundColor: '#f5f5f5',
borderRadius: '4px'
},
body: [
{
"type": "tpl",
tpl: '<h4>匹配函数签名:</h4>',
"inline": false
},
{
"type": "code",
"language": "typescript",
"value": `interface CRUDMatchFunc {
(
/* 当前列表的全量数据 */
items: any,
/* 最近一次接口返回的全量数据 */
itemsRaw: any,
/** 相关配置 */
options?: {
/* 查询参数 */
query: Record<string, any>,
/* 列配置 */
columns: any;
}
): boolean;
}`
}
]
},
{
"type": "crud",
"syncLocation": false,
"api": "/api/mock2/crud/loadDataOnce",
"loadDataOnce": true,
"loadDataOnceFetchOnFilter": false,
"perPage": 5,
"matchFunc": `
const {query = {}, columns} = options;
let result = items.concat();
Object.keys(query).forEach(key => {
const value = query[key];
if (value == null) {
return;
}
if (key === 'status') {
result = result.filter(item => item.status === Boolean(value));
} else if (key === 'time') {
if (typeof value === 'string') {
const [start, end] = value.split(",");
result = result.filter(item => {
const time = Number(item.time);
return time >= Number(start) && time <= Number(end);
});
}
}
});
return result;`,
"filter": {
"debug": true,
"body": [
{
"type": "switch",
"name": "status",
"label": "已核验",
"size": "sm"
},
{
"type": "input-datetime-range",
"name": "time",
"label": "时间",
"size": "full"
}
],
"actions": [
{
"type": "reset",
"label": "重置"
},
{
"type": "submit",
"level": "primary",
"label": "查询"
}
]
},
"columns": [
{
"name": "id",
"label": "ID"
},
{
"name": "browser",
"label": "Browser"
},
{
"name": "version",
"label": "Engine version",
"searchable": {
"type": "select",
"name": "version",
"label": "Engine version",
"clearable": true,
"multiple": true,
"searchable": true,
"checkAll": true,
"options": [
"1.7",
"3.3",
"5.6"
],
"maxTagCount": 10,
"extractValue": true,
"joinValues": false,
"delimiter": ",",
"defaultCheckAll": false,
"checkAllLabel": "全选"
}
},
{
"name": "grade",
"label": "CSS grade"
},
{
"name": "status",
"label": "已核验",
"type": "tpl",
"tpl": "${status === true ? '是' : '否'}",
"filterable": {
"options": [
{"label": "是", "value": true},
{"label": "否", "value": false}
]
}
},
{
"name": "time",
"type": "date",
"label": "时间",
"format": "YYYY-MM-DD HH:mm:ss"
}
]
},
{
"type": "divider",
},
{
"type": "tpl",
tpl: '<h2>使用数据域变量作为数据源:</h2>',
"inline": false
},
{
"type": "service",
"api": {
"url": "/api/mock2/crud/loadDataOnce",
"method": "get",
"responseData": {
"table": "${rows}"
}
},
"body": [
{
"type": "crud",
"syncLocation": false,
"source": "${table}",
"loadDataOnce": true,
"loadDataOnceFetchOnFilter": false,
"perPage": 5,
"matchFunc": `
const {query = {}, columns} = options;
let result = itemsRaw.concat();
Object.keys(query).forEach(key => {
const value = query[key];
if (value == null) {
return;
}
if (key === 'status') {
result = result.filter(item => item.status === Boolean(value));
} else if (key === 'time') {
if (typeof value === 'string') {
const [start, end] = value.split(",");
result = result.filter(item => {
const time = Number(item.time);
return time >= Number(start) && time <= Number(end);
});
}
}
});
return result;`,
"filter": {
"debug": true,
"body": [
{
"type": "switch",
"name": "status",
"label": "已核验",
"size": "sm"
},
{
"type": "input-datetime-range",
"name": "time",
"label": "时间",
"size": "full"
}
],
"actions": [
{
"type": "reset",
"label": "重置"
},
{
"type": "submit",
"level": "primary",
"label": "查询"
}
]
},
"columns": [
{
"name": "id",
"label": "ID"
},
{
"name": "browser",
"label": "Browser"
},
{
"name": "version",
"label": "Engine version",
"searchable": {
"type": "select",
"name": "version",
"label": "Engine version",
"clearable": true,
"multiple": true,
"searchable": true,
"checkAll": true,
"options": [
"1.7",
"3.3",
"5.6"
],
"maxTagCount": 10,
"extractValue": true,
"joinValues": false,
"delimiter": ",",
"defaultCheckAll": false,
"checkAllLabel": "全选"
}
},
{
"name": "grade",
"label": "CSS grade"
},
{
"name": "status",
"label": "已核验",
"type": "tpl",
"tpl": "${status === true ? '是' : '否'}",
"filterable": {
"options": [
{"label": "是", "value": true},
{"label": "否", "value": false}
]
}
},
{
"name": "time",
"type": "date",
"label": "时间",
"format": "YYYY-MM-DD HH:mm:ss"
}
]
}
]
}
]
};

View File

@ -58,6 +58,7 @@ import ExportCSVExcelSchema from './CRUD/ExportCSVExcel';
import CRUDDynamicSchema from './CRUD/Dynamic';
import CRUDSimplePagerSchema from './CRUD/SimplePager';
import CRUDParsePrimitiveQuerySchema from './CRUD/ParsePrimitiveQuery';
import CRUDMatchFuncSchema from './CRUD/MatchFunc';
import ItemActionchema from './CRUD/ItemAction';
import SdkTest from './Sdk/Test';
import JSONSchemaForm from './Form/Schem';
@ -438,9 +439,20 @@ export const examples = [
component: makeSchemaRenderer(PopOverCrudSchema)
},
{
label: '一次性加载',
path: '/examples/crud/load-once',
component: makeSchemaRenderer(LoadOnceTableCrudSchema)
label: '前端分页',
icon: 'fa fa-list-ol',
children: [
{
label: '一次性加载',
path: '/examples/crud/load-once',
component: makeSchemaRenderer(LoadOnceTableCrudSchema)
},
{
label: '匹配函数',
path: '/examples/crud/match-func',
component: makeSchemaRenderer(CRUDMatchFuncSchema)
}
]
},
{
label: '点击联动',

View File

@ -0,0 +1,552 @@
/** 前端分页的接口 */
module.exports = function (req, res) {
const perPage = 10;
const page = req.query.page || 1;
let items = data.concat();
const validQueryKey = Object.keys(req.query).filter(
item => item !== 'keywords' && req.query[item] != null
);
if (validQueryKey.length > 0) {
items = items.filter(item =>
validQueryKey.every(key => {
if (key === 'status') {
return item[key] === (req.query[key] === 'true' ? true : false);
}
if (key === 'time') {
const [start, end] = req.query[key];
return Number(itme[key]) >= Number(start) && Number(itme[key]) <= Number(end);
}
return !!~req.query[key].indexOf(item[key] || '');
})
);
}
const ret = {
status: 0,
msg: 'ok',
data: {
count: items.length,
rows: items
}
};
res.json(ret);
};
const data = [
{
"browser": "Internet Explorer 4.0",
"platform": "Win 95+",
"version": "4",
"grade": "X",
"status": true,
"time": "1698364800"
},
{
"browser": "Internet Explorer 5.0",
"platform": "Win 95+",
"version": "5",
"grade": "C",
"status": false,
"time": "1698364800"
},
{
"browser": "Internet Explorer 5.5",
"platform": "Win 95+",
"version": "5.5",
"grade": "A",
"status": true,
"time": "1698364800"
},
{
"engine": "Trident",
"browser": "Internet Explorer 6",
"version": "6",
"grade": "A",
"status": false,
"time": "1698364800"
},
{
"engine": "Trident",
"browser": "Internet Explorer 7",
"version": "7",
"grade": "A",
"status": true,
"time": "1698364800"
},
{
"engine": "Trident",
"browser": "AOL browser (AOL desktop)",
"platform": "Win XP",
"grade": "A",
"version": "1",
"status": false,
"time": "1698364800"
},
{
"engine": "Gecko",
"browser": "Firefox 1.0",
"platform": "Win 98+ / OSX.2+",
"grade": "A",
"version": "1.8",
"status": true,
"time": "1698364800"
},
{
"engine": "Gecko",
"browser": "Firefox 1.5",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"status": false,
"time": "1698364800"
},
{
"engine": "Gecko",
"browser": "Firefox 2.0",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"status": true,
"time": "1698364800"
},
{
"engine": "Gecko",
"browser": "Firefox 3.0",
"platform": "Win 2k+ / OSX.3+",
"version": "1.9",
"grade": "A",
"status": false,
"time": "1698364800"
},
{
"engine": "Gecko",
"browser": "Camino 1.0",
"platform": "OSX.2+",
"version": "1.8",
"grade": "A",
"status": true,
"time": "1698364800"
},
{
"engine": "Gecko",
"browser": "Camino 1.5",
"platform": "OSX.3+",
"version": "1.8",
"grade": "A",
"status": false,
"time": "1698364800"
},
{
"engine": "Gecko",
"browser": "Netscape 7.2",
"platform": "Win 95+ / Mac OS 8.6-9.2",
"version": "1.7",
"grade": "A",
"status": true,
"time": "1698364800"
},
{
"engine": "Gecko",
"browser": "Netscape Browser 8",
"platform": "Win 98SE+",
"version": "1.7",
"grade": "A",
"status": false,
"time": "1698796800"
},
{
"engine": "Gecko",
"browser": "Netscape Navigator 9",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A",
"status": true,
"time": "1698796800"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.0",
"platform": "Win 95+ / OSX.1+",
"version": "1",
"grade": "A",
"status": false,
"time": "1698796800"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.1",
"platform": "Win 95+ / OSX.1+",
"version": "1.1",
"grade": "A",
"status": true,
"time": "1698796800"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.2",
"platform": "Win 95+ / OSX.1+",
"version": "1.2",
"grade": "A",
"status": false,
"time": "1698969600"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.3",
"platform": "Win 95+ / OSX.1+",
"version": "1.3",
"grade": "A",
"status": true,
"time": "1698969600"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.4",
"platform": "Win 95+ / OSX.1+",
"version": "1.4",
"grade": "A",
"status": false,
"time": "1698969600"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.5",
"platform": "Win 95+ / OSX.1+",
"version": "1.5",
"grade": "A",
"status": true,
"time": "1698969600"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.6",
"platform": "Win 95+ / OSX.1+",
"version": "1.6",
"grade": "A",
"status": false,
"time": "1698969600"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.7",
"platform": "Win 98+ / OSX.1+",
"version": "1.7",
"grade": "A",
"status": true,
"time": "1701388800"
},
{
"engine": "Gecko",
"browser": "Mozilla 1.8",
"platform": "Win 98+ / OSX.1+",
"version": "1.8",
"grade": "A",
"status": false,
"time": "1701388800"
},
{
"engine": "Gecko",
"browser": "Seamonkey 1.1",
"platform": "Win 98+ / OSX.2+",
"version": "1.8",
"grade": "A",
"status": true,
"time": "1701388800"
},
{
"engine": "Gecko",
"browser": "Epiphany 2.20",
"platform": "Gnome",
"version": "1.8",
"grade": "A",
"status": false,
"time": "1701388800"
},
{
"engine": "Webkit",
"browser": "Safari 1.2",
"platform": "OSX.3",
"version": "125.5",
"grade": "A",
"status": true,
"time": "1701388800"
},
{
"engine": "Webkit",
"browser": "Safari 1.3",
"platform": "OSX.3",
"version": "312.8",
"grade": "A",
"status": false,
"time": "1702166400"
},
{
"engine": "Webkit",
"browser": "Safari 2.0",
"platform": "OSX.4+",
"version": "419.3",
"grade": "A",
"status": true,
"time": "1702166400"
},
{
"engine": "Webkit",
"browser": "Safari 3.0",
"platform": "OSX.4+",
"version": "522.1",
"grade": "A",
"status": false,
"time": "1702166400"
},
{
"engine": "Webkit",
"browser": "OmniWeb 5.5",
"platform": "OSX.4+",
"version": "420",
"grade": "A",
"status": true,
"time": "1702166400"
},
{
"engine": "Webkit",
"browser": "iPod Touch / iPhone",
"platform": "iPod",
"version": "420.1",
"grade": "A",
"status": false,
"time": "1702166400"
},
{
"engine": "Webkit",
"browser": "S60",
"platform": "S60",
"version": "413",
"grade": "A",
"status": true,
"time": "1702166400"
},
{
"engine": "Presto",
"browser": "Opera 7.0",
"platform": "Win 95+ / OSX.1+",
"version": "-",
"grade": "A",
"status": false,
"time": "1702771200"
},
{
"engine": "Presto",
"browser": "Opera 7.5",
"platform": "Win 95+ / OSX.2+",
"version": "-",
"grade": "A",
"status": true,
"time": "1702771200"
},
{
"engine": "Presto",
"browser": "Opera 8.0",
"platform": "Win 95+ / OSX.2+",
"version": "-",
"grade": "A",
"status": false,
"time": "1702771200"
},
{
"engine": "Presto",
"browser": "Opera 8.5",
"platform": "Win 95+ / OSX.2+",
"version": "-",
"grade": "A",
"status": true,
"time": "1702771200"
},
{
"engine": "Presto",
"browser": "Opera 9.0",
"platform": "Win 95+ / OSX.3+",
"version": "-",
"grade": "A",
"status": false,
"time": "1702771200"
},
{
"engine": "Presto",
"browser": "Opera 9.2",
"platform": "Win 88+ / OSX.3+",
"version": "-",
"grade": "A",
"status": true,
"time": "1702771200"
},
{
"engine": "Presto",
"browser": "Opera 9.5",
"platform": "Win 88+ / OSX.3+",
"version": "-",
"grade": "A",
"status": false,
"time": "1702771200"
},
{
"engine": "Presto",
"browser": "Opera for Wii",
"platform": "Wii",
"version": "-",
"grade": "A",
"status": true,
"time": "1702771200"
},
{
"engine": "Presto",
"browser": "Nokia N800",
"platform": "N800",
"version": "-",
"grade": "A",
"status": false,
"time": "1702771200"
},
{
"engine": "Presto",
"browser": "Nintendo DS browser",
"platform": "Nintendo DS",
"version": "8.5",
"grade": "C",
"status": true,
"time": "1703548800"
},
{
"engine": "KHTML",
"browser": "Konqureror 3.1",
"platform": "KDE 3.1",
"version": "3.1",
"grade": "C",
"status": false,
"time": "1703548800"
},
{
"engine": "KHTML",
"browser": "Konqureror 3.3",
"platform": "KDE 3.3",
"version": "3.3",
"grade": "A",
"status": true,
"time": "1703548800"
},
{
"engine": "KHTML",
"browser": "Konqureror 3.5",
"platform": "KDE 3.5",
"version": "3.5",
"grade": "A",
"status": false,
"time": "1703548800"
},
{
"engine": "Tasman",
"browser": "Internet Explorer 4.5",
"platform": "Mac OS 8-9",
"version": "-",
"grade": "X",
"status": true,
"time": "1703548800"
},
{
"engine": "Tasman",
"browser": "Internet Explorer 5.1",
"platform": "Mac OS 7.6-9",
"version": "1",
"grade": "C",
"status": false,
"time": "1703548800"
},
{
"engine": "Tasman",
"browser": "Internet Explorer 5.2",
"platform": "Mac OS 8-X",
"version": "1",
"grade": "C",
"status": true,
"time": "1703548800"
},
{
"engine": "Misc",
"browser": "NetFront 3.1",
"platform": "Embedded devices",
"version": "-",
"grade": "C",
"status": false,
"time": "1703548800"
},
{
"engine": "Misc",
"browser": "NetFront 3.4",
"platform": "Embedded devices",
"version": "-",
"grade": "A",
"status": true,
"time": "1703548800"
},
{
"engine": "Misc",
"browser": "Dillo 0.8",
"platform": "Embedded devices",
"version": "-",
"grade": "X",
"status": false,
"time": "1703548800"
},
{
"engine": "Misc",
"browser": "Links",
"platform": "Text only",
"version": "-",
"grade": "X",
"status": true,
"time": "1703721600"
},
{
"engine": "Misc",
"browser": "Lynx",
"platform": "Text only",
"version": "-",
"grade": "X",
"status": false,
"time": "1703721600"
},
{
"engine": "Misc",
"browser": "IE Mobile",
"platform": "Windows Mobile 6",
"version": "-",
"grade": "C",
"status": true,
"time": "1703721600"
},
{
"engine": "Misc",
"browser": "PSP browser",
"platform": "PSP",
"version": "-",
"grade": "C",
"status": false,
"time": "1703721600"
},
{
"engine": "Other browsers",
"browser": "All others",
"platform": "-",
"version": "-",
"grade": "U",
"status": true,
"time": "1703721600"
}
].map(function (item, index) {
return Object.assign({}, item, {
id: index + 1
});
});

View File

@ -18,6 +18,22 @@ import {normalizeApiResponseData} from '../utils/api';
import {matchSorter} from 'match-sorter';
import {filter} from '../utils/tpl';
interface MatchFunc {
(
/* 当前列表的全量数据 */
items: any,
/* 最近一次接口返回的全量数据 */
itemsRaw: any,
/** 相关配置 */
options?: {
/* 查询参数 */
query: Record<string, any>;
/* 列配置 */
columns: any;
}
): any;
}
class ServerError extends Error {
type = 'ServerError';
}
@ -161,10 +177,12 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
loadDataMode?: boolean;
syncResponse2Query?: boolean;
columns?: Array<any>;
matchFunc?: MatchFunc;
} = {}
) {
try {
if (!options.forceReload && options.loadDataOnce && self.total) {
const matchFunc = options.matchFunc;
let items = options.source
? resolveVariableAndFilter(
options.source,
@ -176,41 +194,49 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
)
: self.items.concat();
if (Array.isArray(options.columns)) {
options.columns.forEach((column: any) => {
let value: any =
typeof column.name === 'string'
? getVariable(self.query, column.name)
: undefined;
const key = column.name;
if (value != null && key) {
// value可能为null、undefined、''、0
if (Array.isArray(value)) {
if (value.length > 0) {
const arr = [...items];
let arrItems: Array<any> = [];
value.forEach(item => {
arrItems = [
...arrItems,
...matchSorter(arr, item, {
keys: [key],
threshold: matchSorter.rankings.CONTAINS
})
];
});
items = items.filter((item: any) =>
arrItems.find(a => a === item)
);
}
} else {
items = matchSorter(items, value, {
keys: [key],
threshold: matchSorter.rankings.CONTAINS
});
}
}
/** 字段的格式类型无法穷举,所以支持使用函数过滤 */
if (matchFunc && typeof matchFunc === 'function') {
items = matchFunc(items, self.data.itemsRaw, {
query: self.query,
columns: options.columns
});
} else {
if (Array.isArray(options.columns)) {
options.columns.forEach((column: any) => {
let value: any =
typeof column.name === 'string'
? getVariable(self.query, column.name)
: undefined;
const key = column.name;
if (value != null && key) {
// value可能为null、undefined、''、0
if (Array.isArray(value)) {
if (value.length > 0) {
const arr = [...items];
let arrItems: Array<any> = [];
value.forEach(item => {
arrItems = [
...arrItems,
...matchSorter(arr, item, {
keys: [key],
threshold: matchSorter.rankings.CONTAINS
})
];
});
items = items.filter((item: any) =>
arrItems.find(a => a === item)
);
}
} else {
items = matchSorter(items, value, {
keys: [key],
threshold: matchSorter.rankings.CONTAINS
});
}
}
});
}
}
if (self.query.orderBy) {
@ -589,8 +615,10 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
source: string,
options: {
columns?: Array<any>;
matchFunc?: MatchFunc | null;
}
) {
const matchFunc = options.matchFunc;
let items: Array<any> = resolveVariableAndFilter(source, scope, '| raw');
if (!Array.isArray(items) && !self.items.length) {
@ -599,41 +627,49 @@ export const CRUDStore = ServiceStore.named('CRUDStore')
items = Array.isArray(items) ? items : [];
if (Array.isArray(options.columns)) {
options.columns.forEach((column: any) => {
let value: any =
typeof column.name === 'string'
? getVariable(self.query, column.name)
: undefined;
const key = column.name;
if (value != null && key) {
// value可能为null、undefined、''、0
if (Array.isArray(value)) {
if (value.length > 0) {
const arr = [...items];
let arrItems: Array<any> = [];
value.forEach(item => {
arrItems = [
...arrItems,
...matchSorter(arr, item, {
keys: [key],
threshold: matchSorter.rankings.CONTAINS
})
];
});
items = items.filter((item: any) =>
arrItems.find(a => a === item)
);
}
} else {
items = matchSorter(items, value, {
keys: [key],
threshold: matchSorter.rankings.CONTAINS
});
}
}
/** 字段的格式类型无法穷举,所以支持使用函数过滤 */
if (matchFunc && typeof matchFunc === 'function') {
items = matchFunc(items, items.concat(), {
query: self.query,
columns: options.columns
});
} else {
if (Array.isArray(options.columns)) {
options.columns.forEach((column: any) => {
let value: any =
typeof column.name === 'string'
? getVariable(self.query, column.name)
: undefined;
const key = column.name;
if (value != null && key) {
// value可能为null、undefined、''、0
if (Array.isArray(value)) {
if (value.length > 0) {
const arr = [...items];
let arrItems: Array<any> = [];
value.forEach(item => {
arrItems = [
...arrItems,
...matchSorter(arr, item, {
keys: [key],
threshold: matchSorter.rankings.CONTAINS
})
];
});
items = items.filter((item: any) =>
arrItems.find(a => a === item)
);
}
} else {
items = matchSorter(items, value, {
keys: [key],
threshold: matchSorter.rankings.CONTAINS
});
}
}
});
}
}
if (self.query.orderBy) {

View File

@ -329,6 +329,12 @@ export interface CRUDCommonSchema extends BaseSchema, SpinnerExtraProps {
*/
loadDataOnceFetchOnFilter?: boolean;
/**
* loadDataOnce时
* @since 3.5.0
*/
matchFunc?: string | any;
/**
*
*/
@ -473,7 +479,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
'autoFillHeight',
'maxTagCount',
'overflowTagPopover',
'parsePrimitiveQuery'
'parsePrimitiveQuery',
'matchFunc'
];
static defaultProps = {
toolbarInline: true,
@ -1244,6 +1251,15 @@ export default class CRUD extends React.Component<CRUDProps, any> {
);
this.lastQuery = store.query;
const data = createObject(store.data, store.query);
const matchFunc =
this.props?.matchFunc && typeof this.props.matchFunc === 'string'
? (str2function(
this.props.matchFunc,
'items',
'itemsRaw',
'options'
) as any)
: undefined;
isEffectiveApi(api, data)
? store
.fetchInitData(api, data, {
@ -1258,7 +1274,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
perPageField,
loadDataMode,
syncResponse2Query,
columns: store.columns ?? columns
columns: store.columns ?? columns,
matchFunc
})
.then(async value => {
if (!isAlive(store)) {
@ -1332,7 +1349,8 @@ export default class CRUD extends React.Component<CRUDProps, any> {
})
: source &&
store.initFromScope(data, source, {
columns: store.columns ?? columns
columns: store.columns ?? columns,
matchFunc
});
}