@hzab/data-model 1.8.7 → 1.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -1
- package/package.json +2 -1
- package/src/ArrayUtils.js +324 -105
- package/src/array-data-model.js +13 -2
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hzab/data-model",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.8",
|
|
4
4
|
"description": "data model",
|
|
5
5
|
"main": "src",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "npm run prepare && webpack serve -c ./config/webpack.config.js --env local",
|
|
8
8
|
"build": "webpack -c ./config/webpack.config.js --env production",
|
|
9
|
+
"publish-beta": "npm publish --beta",
|
|
9
10
|
"publish-patch": "npm version patch && npm publish --access public",
|
|
10
11
|
"publish-minor": "npm version minor && npm publish --access public",
|
|
11
12
|
"publish-major": "npm version major && npm publish --access public",
|
package/src/ArrayUtils.js
CHANGED
|
@@ -30,7 +30,7 @@ export class ArrayUtils {
|
|
|
30
30
|
* @returns
|
|
31
31
|
*/
|
|
32
32
|
findItemById(id) {
|
|
33
|
-
if (
|
|
33
|
+
if (this._hasId(id)) {
|
|
34
34
|
return null;
|
|
35
35
|
}
|
|
36
36
|
const { _idKey } = this;
|
|
@@ -42,99 +42,157 @@ export class ArrayUtils {
|
|
|
42
42
|
* @param {*} id
|
|
43
43
|
* @returns
|
|
44
44
|
*/
|
|
45
|
-
findItem(query = {}) {
|
|
46
|
-
const {
|
|
47
|
-
const id = query[_idKey];
|
|
48
|
-
|
|
45
|
+
findItem(query = {}, options = {}) {
|
|
46
|
+
const { fuzzy = false, ignoreCase = false } = options;
|
|
47
|
+
const id = query[this._idKey];
|
|
48
|
+
|
|
49
|
+
if (!this._hasId(id)) {
|
|
49
50
|
return this.findItemById(id);
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
|
|
53
|
+
const validQuery = this._filterEmptyQuery(query);
|
|
54
|
+
if (_.isEmpty(validQuery)) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const matchItem = this._list.find((item) => {
|
|
59
|
+
return this._isItemMatchQuery(item, validQuery, { fuzzy, ignoreCase });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return matchItem ? _.cloneDeep(matchItem) : null;
|
|
53
63
|
}
|
|
54
64
|
|
|
55
65
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* @param {
|
|
66
|
+
* 通过 query 筛选列表(支持多条件筛选、分页、排序)
|
|
67
|
+
* @param {Object} query - 综合查询参数
|
|
68
|
+
* @param {number} [query.pageNum=1] - 页码(默认1)
|
|
69
|
+
* @param {number} [query.pageSize=10] - 每页条数(默认10)
|
|
70
|
+
* @param {string|string[]} [query.sortKey] - 排序字段(支持嵌套路径,如 'user.age')
|
|
71
|
+
* @param {string} [query.sortType='desc'] - 排序方式(desc/asc)
|
|
72
|
+
* @param {string} [query.orderByColumn] - 兼容旧排序字段参数
|
|
73
|
+
* @param {string} [query.isAsc] - 兼容旧排序方式参数
|
|
74
|
+
* @param {Object} [options={}] - 查询选项
|
|
75
|
+
* @param {boolean} [options.fuzzy=false] - 字符串字段是否开启模糊匹配
|
|
76
|
+
* @param {boolean} [options.ignoreCase=true] - 模糊匹配时是否忽略大小写
|
|
77
|
+
* @returns {Object} 分页结果
|
|
78
|
+
* @returns {Object[]} result.list - 筛选后的列表(深拷贝)
|
|
79
|
+
* @returns {Object} result.pagination - 分页信息(current/total/pageSize)
|
|
59
80
|
*/
|
|
60
|
-
findListByQuery(query) {
|
|
61
|
-
const {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
81
|
+
findListByQuery(query = {}, options = {}) {
|
|
82
|
+
const {
|
|
83
|
+
pageNum = 1,
|
|
84
|
+
pageSize = 10,
|
|
85
|
+
sortKey,
|
|
86
|
+
sortType,
|
|
87
|
+
orderByColumn,
|
|
88
|
+
isAsc,
|
|
89
|
+
...filterQuery // 提取筛选条件(排除分页排序参数)
|
|
90
|
+
} = query;
|
|
91
|
+
|
|
92
|
+
const { fuzzy = true, ignoreCase = false } = options;
|
|
93
|
+
|
|
94
|
+
// 数据筛选(基于 query 条件)
|
|
95
|
+
let filteredList = [...this._list];
|
|
96
|
+
const validFilterQuery = this._filterEmptyQuery(filterQuery);
|
|
97
|
+
|
|
98
|
+
if (!_.isEmpty(validFilterQuery)) {
|
|
99
|
+
filteredList = filteredList.filter((item) => {
|
|
100
|
+
return this._isItemMatchQuery(item, validFilterQuery, { fuzzy, ignoreCase });
|
|
101
|
+
});
|
|
75
102
|
}
|
|
76
|
-
// 计算结束索引
|
|
77
|
-
const endIndex = startIndex + pageSize;
|
|
78
103
|
|
|
79
|
-
//
|
|
80
|
-
|
|
104
|
+
// 数据排序(基于筛选后的数据)
|
|
105
|
+
const sortedList = this.getSortList(
|
|
106
|
+
sortKey || orderByColumn,
|
|
107
|
+
sortType || isAsc,
|
|
108
|
+
filteredList, // 传入筛选后列表,避免排序影响原数据
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// 分页处理(边界校验)
|
|
112
|
+
const pagination = {
|
|
113
|
+
current: Math.max(1, pageNum), // 页码最小为1
|
|
114
|
+
pageSize: Math.max(1, pageSize), // 每页条数最小为1
|
|
115
|
+
total: filteredList.length,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// 计算分页截取范围
|
|
119
|
+
const startIndex = (pagination.current - 1) * pagination.pageSize;
|
|
120
|
+
const endIndex = startIndex + pagination.pageSize;
|
|
121
|
+
const paginatedList = sortedList.slice(startIndex, endIndex);
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
list: _.cloneDeep(paginatedList),
|
|
125
|
+
pagination: { ...pagination },
|
|
126
|
+
};
|
|
81
127
|
}
|
|
82
128
|
|
|
83
129
|
/**
|
|
84
130
|
* 对目标数据进行多维度排序
|
|
85
|
-
* @param
|
|
86
|
-
* @param {
|
|
87
|
-
* @param {
|
|
88
|
-
* @returns
|
|
131
|
+
* @param {string|string[]|Object[]} sortKey - 排序字段配置
|
|
132
|
+
* @param {string} sortType - 排序方式(desc/asc)
|
|
133
|
+
* @param {Object[]} [customList] - 自定义排序列表(默认使用内部 _list)
|
|
134
|
+
* @returns {Object[]} 排序后的列表
|
|
89
135
|
*/
|
|
90
|
-
getSortList(sortKey, sortType) {
|
|
91
|
-
const
|
|
136
|
+
getSortList(sortKey, sortType, customList) {
|
|
137
|
+
const sourceList = customList || this._list;
|
|
138
|
+
const _list = _.cloneDeep(sourceList);
|
|
139
|
+
|
|
140
|
+
// 无排序条件时直接返回原列表
|
|
92
141
|
if (!sortKey && !sortType) {
|
|
93
142
|
return _list;
|
|
94
143
|
}
|
|
144
|
+
|
|
145
|
+
// 统一排序字段格式为数组
|
|
95
146
|
const paths = Array.isArray(sortKey) ? sortKey : [sortKey];
|
|
147
|
+
|
|
96
148
|
_list.sort((a, b) => {
|
|
97
|
-
return this.compareData(a, b, { paths, sortType });
|
|
149
|
+
return this.compareData(a, b, { paths, sortType, pathIdx: 0 });
|
|
98
150
|
});
|
|
151
|
+
|
|
99
152
|
return _list;
|
|
100
153
|
}
|
|
101
154
|
|
|
102
155
|
/**
|
|
103
|
-
*
|
|
104
|
-
* @param dataA
|
|
105
|
-
* @param dataB
|
|
106
|
-
* @param opt
|
|
107
|
-
* @param opt.paths
|
|
108
|
-
* @param opt.pathIdx
|
|
109
|
-
* @param opt.sortType
|
|
110
|
-
* @returns
|
|
156
|
+
* 比较两个数据对象的排序顺序(支持多字段优先级排序)
|
|
157
|
+
* @param {Object} dataA - 比较对象A
|
|
158
|
+
* @param {Object} dataB - 比较对象B
|
|
159
|
+
* @param {Object} opt - 比较配置
|
|
160
|
+
* @param {string[]|Object[]} opt.paths - 排序字段路径数组
|
|
161
|
+
* @param {number} opt.pathIdx - 当前比较的字段索引
|
|
162
|
+
* @param {string} opt.sortType - 排序方式(desc/asc)
|
|
163
|
+
* @returns {number} 排序结果(-1/0/1)
|
|
111
164
|
*/
|
|
112
|
-
compareData(dataA, dataB, opt = {
|
|
165
|
+
compareData(dataA, dataB, opt = {}) {
|
|
113
166
|
const { paths, pathIdx = 0, sortType = "desc" } = opt;
|
|
114
|
-
const
|
|
115
|
-
let _path = path;
|
|
167
|
+
const currentPath = paths[pathIdx];
|
|
116
168
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
_path = path.path;
|
|
169
|
+
// 处理自定义比较逻辑
|
|
170
|
+
if (currentPath && typeof currentPath === "object" && typeof currentPath.customCompare === "function") {
|
|
171
|
+
const direction = sortType === "asc" ? -1 : 1;
|
|
172
|
+
return direction * currentPath.customCompare(dataA, dataB, opt);
|
|
123
173
|
}
|
|
124
174
|
|
|
125
|
-
|
|
175
|
+
// 提取实际字段路径(支持配置对象格式)
|
|
176
|
+
const path = typeof currentPath === "object" ? currentPath.path : currentPath;
|
|
177
|
+
if (!path) {
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 确定排序方向(支持按字段单独配置排序方式)
|
|
126
182
|
let direction = 1;
|
|
127
|
-
// 兼容 string 及 object 类型
|
|
128
183
|
let _sortType = sortType;
|
|
129
184
|
if (typeof _sortType === "object") {
|
|
130
|
-
_sortType = _sortType[
|
|
185
|
+
_sortType = _sortType[path] || "desc";
|
|
131
186
|
}
|
|
132
187
|
if (_sortType === "asc") {
|
|
133
188
|
direction = -1;
|
|
134
189
|
}
|
|
135
190
|
|
|
136
|
-
|
|
137
|
-
|
|
191
|
+
// 获取并处理字段值(支持日期转换、自定义格式化)
|
|
192
|
+
const valueA = this.handleValByKey(dataA, currentPath);
|
|
193
|
+
const valueB = this.handleValByKey(dataB, currentPath);
|
|
194
|
+
|
|
195
|
+
// 数值比较
|
|
138
196
|
if (valueA < valueB) {
|
|
139
197
|
return -1 * direction;
|
|
140
198
|
}
|
|
@@ -142,43 +200,53 @@ export class ArrayUtils {
|
|
|
142
200
|
return 1 * direction;
|
|
143
201
|
}
|
|
144
202
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (valueA == valueB) {
|
|
150
|
-
// 继续使用次选排序规则排序
|
|
203
|
+
// 当前字段值相等时,使用下一个优先级字段排序
|
|
204
|
+
if (pathIdx + 1 < paths.length) {
|
|
151
205
|
return this.compareData(dataA, dataB, { ...opt, pathIdx: pathIdx + 1 });
|
|
152
206
|
}
|
|
207
|
+
|
|
208
|
+
// 所有字段都相等时,保持原有顺序
|
|
209
|
+
return 0;
|
|
153
210
|
}
|
|
154
211
|
|
|
155
212
|
/**
|
|
156
|
-
*
|
|
157
|
-
* @param {Object} data
|
|
158
|
-
* @param {string
|
|
159
|
-
* @param {string}
|
|
160
|
-
* @param {string}
|
|
161
|
-
* @param {Function}
|
|
213
|
+
* 根据路径获取并处理字段值(支持嵌套路径、类型转换、自定义格式化)
|
|
214
|
+
* @param {Object} data - 数据源对象
|
|
215
|
+
* @param {string|Object} pathConfig - 字段配置
|
|
216
|
+
* @param {string} [pathConfig.path] - 字段路径(如 'user.address.city')
|
|
217
|
+
* @param {string} [pathConfig.type] - 字段类型(支持 'date' 自动转时间戳)
|
|
218
|
+
* @param {Function} [pathConfig.customFormat] - 自定义值处理函数
|
|
219
|
+
* @returns {*} 处理后的字段值
|
|
162
220
|
*/
|
|
163
|
-
handleValByKey(data,
|
|
164
|
-
|
|
165
|
-
|
|
221
|
+
handleValByKey(data, pathConfig) {
|
|
222
|
+
// 处理字符串路径
|
|
223
|
+
if (typeof pathConfig === "string") {
|
|
224
|
+
return _.get(data, pathConfig);
|
|
166
225
|
}
|
|
167
|
-
|
|
168
|
-
|
|
226
|
+
|
|
227
|
+
// 处理空配置
|
|
228
|
+
if (_.isNil(pathConfig)) {
|
|
229
|
+
return undefined;
|
|
169
230
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
231
|
+
|
|
232
|
+
// 处理对象配置
|
|
233
|
+
if (typeof pathConfig === "object") {
|
|
234
|
+
let val = _.get(data, pathConfig.path);
|
|
235
|
+
|
|
236
|
+
// 日期类型转换为时间戳(便于数值比较)
|
|
237
|
+
if (pathConfig.type === "date") {
|
|
238
|
+
val = dayjs(val).isValid() ? dayjs(val).valueOf() : 0;
|
|
175
239
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
240
|
+
|
|
241
|
+
// 自定义值格式化
|
|
242
|
+
if (typeof pathConfig.customFormat === "function") {
|
|
243
|
+
val = pathConfig.customFormat(val, data);
|
|
179
244
|
}
|
|
245
|
+
|
|
180
246
|
return val;
|
|
181
247
|
}
|
|
248
|
+
|
|
249
|
+
return undefined;
|
|
182
250
|
}
|
|
183
251
|
|
|
184
252
|
/**
|
|
@@ -202,10 +270,13 @@ export class ArrayUtils {
|
|
|
202
270
|
*/
|
|
203
271
|
pushItem(data) {
|
|
204
272
|
// TODO: id 去重
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
273
|
+
const items = Array.isArray(data) ? data : [data];
|
|
274
|
+
items.forEach((item) => {
|
|
275
|
+
const cloneItem = _.cloneDeep(item);
|
|
276
|
+
this._setId(cloneItem);
|
|
277
|
+
this._setCreateTime(cloneItem);
|
|
278
|
+
this._list.push(cloneItem);
|
|
279
|
+
});
|
|
209
280
|
return STATE_CODE.SUC;
|
|
210
281
|
}
|
|
211
282
|
|
|
@@ -216,8 +287,8 @@ export class ArrayUtils {
|
|
|
216
287
|
unshiftItem(data) {
|
|
217
288
|
// TODO: id 去重
|
|
218
289
|
const item = _.cloneDeep(data);
|
|
219
|
-
this.
|
|
220
|
-
this.
|
|
290
|
+
this._setId(item);
|
|
291
|
+
this._setCreateTime(item);
|
|
221
292
|
this._list.unshift(item);
|
|
222
293
|
return STATE_CODE.SUC;
|
|
223
294
|
}
|
|
@@ -230,7 +301,7 @@ export class ArrayUtils {
|
|
|
230
301
|
replaceItem(data) {
|
|
231
302
|
const { _idKey, _list } = this;
|
|
232
303
|
const id = data[_idKey];
|
|
233
|
-
if (
|
|
304
|
+
if (this._hasId(id)) {
|
|
234
305
|
return STATE_CODE.ERR_INPUT_ID;
|
|
235
306
|
}
|
|
236
307
|
const idx = _list.findIndex((it) => it[_idKey] === id);
|
|
@@ -238,7 +309,7 @@ export class ArrayUtils {
|
|
|
238
309
|
return STATE_CODE.NOT_FOUNT;
|
|
239
310
|
}
|
|
240
311
|
_list.splice(idx, 1, data);
|
|
241
|
-
this.
|
|
312
|
+
this._setUpdateTime(data);
|
|
242
313
|
return STATE_CODE.SUC;
|
|
243
314
|
}
|
|
244
315
|
|
|
@@ -250,7 +321,7 @@ export class ArrayUtils {
|
|
|
250
321
|
updateItemValue(data) {
|
|
251
322
|
const { _idKey, _list } = this;
|
|
252
323
|
const id = data[_idKey];
|
|
253
|
-
if (
|
|
324
|
+
if (this._hasId(id)) {
|
|
254
325
|
return STATE_CODE.ERR_INPUT_ID;
|
|
255
326
|
}
|
|
256
327
|
const item = _list.find((it) => it[_idKey] === id);
|
|
@@ -260,7 +331,7 @@ export class ArrayUtils {
|
|
|
260
331
|
Object.keys(data).forEach((key) => {
|
|
261
332
|
item[key] = data[key];
|
|
262
333
|
});
|
|
263
|
-
this.
|
|
334
|
+
this._setUpdateTime(data);
|
|
264
335
|
return STATE_CODE.SUC;
|
|
265
336
|
}
|
|
266
337
|
|
|
@@ -272,7 +343,7 @@ export class ArrayUtils {
|
|
|
272
343
|
*/
|
|
273
344
|
delItem(id) {
|
|
274
345
|
const { _idKey, _list } = this;
|
|
275
|
-
if (
|
|
346
|
+
if (this._hasId(id)) {
|
|
276
347
|
return STATE_CODE.ERR_INPUT_ID;
|
|
277
348
|
}
|
|
278
349
|
const idx = _list.findIndex((it) => it[_idKey] === id);
|
|
@@ -283,15 +354,165 @@ export class ArrayUtils {
|
|
|
283
354
|
return STATE_CODE.SUC;
|
|
284
355
|
}
|
|
285
356
|
|
|
357
|
+
/**
|
|
358
|
+
* 检查单个 item 是否匹配所有 query 条件
|
|
359
|
+
* @private
|
|
360
|
+
* @param {Object} item - 待检查的列表项
|
|
361
|
+
* @param {Object} query - 查询条件
|
|
362
|
+
* @param {Object} options - 匹配选项
|
|
363
|
+
* @param {boolean} options.fuzzy - 是否开启模糊匹配
|
|
364
|
+
* @param {boolean} options.ignoreCase - 模糊匹配是否忽略大小写
|
|
365
|
+
* @returns {boolean} 是否匹配所有条件
|
|
366
|
+
*/
|
|
367
|
+
_isItemMatchQuery(item, query, { fuzzy, ignoreCase }) {
|
|
368
|
+
// 遍历所有查询条件,必须全部满足才返回 true
|
|
369
|
+
return Object.entries(query).every(([key, targetValue]) => {
|
|
370
|
+
// 获取 item 中对应字段的值(支持嵌套路径,如 'user.name')
|
|
371
|
+
const itemValue = _.get(item, key);
|
|
372
|
+
|
|
373
|
+
// 处理空值情况(null/undefined 仅匹配 null/undefined)
|
|
374
|
+
if (_.isNil(itemValue)) {
|
|
375
|
+
return _.isNil(targetValue);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// 处理数组类型查询(支持 in 操作,如 { status: [1, 2] })
|
|
379
|
+
if (Array.isArray(targetValue)) {
|
|
380
|
+
return targetValue.some((val) => this._compareSingleValue(itemValue, val, fuzzy, ignoreCase));
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// 处理范围查询(如 { age: { $gt: 18, $lt: 30 } })
|
|
384
|
+
if (typeof targetValue === "object" && !Array.isArray(targetValue)) {
|
|
385
|
+
return this._handleRangeQuery(itemValue, targetValue);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// 处理普通值匹配(精确/模糊)
|
|
389
|
+
return this._compareSingleValue(itemValue, targetValue, fuzzy, ignoreCase);
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* 比较单个值是否匹配(支持精确/模糊匹配)
|
|
395
|
+
* @private
|
|
396
|
+
* @param {*} itemValue - 列表项字段值
|
|
397
|
+
* @param {*} targetValue - 查询目标值
|
|
398
|
+
* @param {boolean} fuzzy - 是否模糊匹配
|
|
399
|
+
* @param {boolean} ignoreCase - 是否忽略大小写
|
|
400
|
+
* @returns {boolean} 是否匹配
|
|
401
|
+
*/
|
|
402
|
+
_compareSingleValue(itemValue, targetValue, fuzzy, ignoreCase) {
|
|
403
|
+
// 类型不同时直接不匹配(避免隐式类型转换导致的问题)
|
|
404
|
+
if (typeof itemValue !== typeof targetValue) {
|
|
405
|
+
// 特殊处理:数字和字符串数字的匹配(如 123 和 '123')
|
|
406
|
+
if (
|
|
407
|
+
(typeof itemValue === "number" && typeof targetValue === "string" && !isNaN(Number(targetValue))) ||
|
|
408
|
+
(typeof itemValue === "string" && typeof targetValue === "number" && !isNaN(Number(itemValue)))
|
|
409
|
+
) {
|
|
410
|
+
return Number(itemValue) === targetValue;
|
|
411
|
+
}
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// 字符串处理(支持模糊匹配和大小写忽略)
|
|
416
|
+
if (typeof itemValue === "string" && fuzzy) {
|
|
417
|
+
const itemStr = ignoreCase ? itemValue.toLowerCase() : itemValue;
|
|
418
|
+
const targetStr = ignoreCase ? String(targetValue).toLowerCase() : String(targetValue);
|
|
419
|
+
return itemStr.includes(targetStr);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// 其他类型(数字、布尔等)精确匹配
|
|
423
|
+
return itemValue === targetValue;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* 处理范围查询(如 $gt/$lt/$gte/$lte)
|
|
428
|
+
* @private
|
|
429
|
+
* @param {number|string|Date} itemValue - 列表项字段值
|
|
430
|
+
* @param {Object} rangeConfig - 范围配置({ $gt: 18, $lt: 30 })
|
|
431
|
+
* @returns {boolean} 是否在范围内
|
|
432
|
+
*/
|
|
433
|
+
_handleRangeQuery(itemValue, rangeConfig) {
|
|
434
|
+
const { $gt, $lt, $gte, $lte } = rangeConfig;
|
|
435
|
+
let isValid = true;
|
|
436
|
+
|
|
437
|
+
// 转换为可比较的类型(优先处理日期)
|
|
438
|
+
const compareValue = this._convertToComparableValue(itemValue);
|
|
439
|
+
const convertTarget = (val) => this._convertToComparableValue(val);
|
|
440
|
+
|
|
441
|
+
// 大于(>)
|
|
442
|
+
if ($gt !== undefined) {
|
|
443
|
+
isValid = isValid && compareValue > convertTarget($gt);
|
|
444
|
+
}
|
|
445
|
+
// 小于(<)
|
|
446
|
+
if ($lt !== undefined) {
|
|
447
|
+
isValid = isValid && compareValue < convertTarget($lt);
|
|
448
|
+
}
|
|
449
|
+
// 大于等于(>=)
|
|
450
|
+
if ($gte !== undefined) {
|
|
451
|
+
isValid = isValid && compareValue >= convertTarget($gte);
|
|
452
|
+
}
|
|
453
|
+
// 小于等于(<=)
|
|
454
|
+
if ($lte !== undefined) {
|
|
455
|
+
isValid = isValid && compareValue <= convertTarget($lte);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return isValid;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* 将值转换为可比较的类型(统一数字、日期格式)
|
|
463
|
+
* @private
|
|
464
|
+
* @param {*} value - 待转换的值
|
|
465
|
+
* @returns {number|string} 可比较的值
|
|
466
|
+
*/
|
|
467
|
+
_convertToComparableValue(value) {
|
|
468
|
+
// 日期转换为时间戳
|
|
469
|
+
if (value instanceof Date || dayjs(value).isValid()) {
|
|
470
|
+
return dayjs(value).valueOf();
|
|
471
|
+
}
|
|
472
|
+
// 字符串数字转换为数字
|
|
473
|
+
if (typeof value === "string" && !isNaN(Number(value))) {
|
|
474
|
+
return Number(value);
|
|
475
|
+
}
|
|
476
|
+
return value;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* 过滤空查询条件(移除 undefined/null/空字符串/空数组/空对象)
|
|
481
|
+
* @private
|
|
482
|
+
* @param {Object} query - 原始查询条件
|
|
483
|
+
* @returns {Object} 过滤后的有效查询条件
|
|
484
|
+
*/
|
|
485
|
+
_filterEmptyQuery(query) {
|
|
486
|
+
return Object.entries(query).reduce((acc, [key, value]) => {
|
|
487
|
+
// 保留非空值:
|
|
488
|
+
// 1. 不是 null/undefined
|
|
489
|
+
// 2. 不是空字符串
|
|
490
|
+
// 3. 不是空数组
|
|
491
|
+
// 4. 不是空对象
|
|
492
|
+
if (
|
|
493
|
+
!_.isNil(value) &&
|
|
494
|
+
value !== "" &&
|
|
495
|
+
!(Array.isArray(value) && value.length === 0) &&
|
|
496
|
+
!(typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0)
|
|
497
|
+
) {
|
|
498
|
+
acc[key] = value;
|
|
499
|
+
}
|
|
500
|
+
return acc;
|
|
501
|
+
}, {});
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
_hasId(id) {
|
|
505
|
+
return _.isNil(id) || id === "";
|
|
506
|
+
}
|
|
507
|
+
|
|
286
508
|
/**
|
|
287
509
|
* 子项没有 id 的时候自动添加
|
|
288
510
|
* @param {*} item
|
|
289
511
|
* @returns
|
|
290
512
|
*/
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
item[_idKey] = nanoid();
|
|
513
|
+
_setId(item) {
|
|
514
|
+
if (_.isObject(item) && this._hasId(item[this._idKey])) {
|
|
515
|
+
item[this._idKey] = nanoid();
|
|
295
516
|
}
|
|
296
517
|
return item;
|
|
297
518
|
}
|
|
@@ -300,21 +521,19 @@ export class ArrayUtils {
|
|
|
300
521
|
* 设置创建时间
|
|
301
522
|
* @param {*} item
|
|
302
523
|
*/
|
|
303
|
-
|
|
304
|
-
item
|
|
524
|
+
_setCreateTime(item) {
|
|
525
|
+
if (_.isObject(item) && this._hasId(item.createTime)) {
|
|
526
|
+
item.createTime = Date.now();
|
|
527
|
+
}
|
|
305
528
|
}
|
|
306
529
|
|
|
307
530
|
/**
|
|
308
531
|
* 设置更新时间
|
|
309
532
|
* @param {*} item
|
|
310
533
|
*/
|
|
311
|
-
|
|
534
|
+
_setUpdateTime(item) {
|
|
312
535
|
item.createTime = Date.now();
|
|
313
536
|
}
|
|
314
537
|
}
|
|
315
538
|
|
|
316
|
-
export const hasId = (id) => {
|
|
317
|
-
return _.isNil(id) || id === "";
|
|
318
|
-
};
|
|
319
|
-
|
|
320
539
|
export default ArrayUtils;
|
package/src/array-data-model.js
CHANGED
|
@@ -107,6 +107,17 @@ class ArrayDataModel extends ArrayUtils {
|
|
|
107
107
|
this.multipleDeleteReqMap = multipleDeleteReqMap;
|
|
108
108
|
/** DELETE 批量删除 接口请求结果数据处理回调 */
|
|
109
109
|
this.multipleDeleteResMap = multipleDeleteResMap;
|
|
110
|
+
|
|
111
|
+
const {
|
|
112
|
+
/**
|
|
113
|
+
*
|
|
114
|
+
* @param {Object} [options={}] - 查询选项
|
|
115
|
+
* @param {boolean} [options.fuzzy=false] - 字符串字段是否开启模糊匹配
|
|
116
|
+
* @param {boolean} [options.ignoreCase=true] - 模糊匹配时是否忽略大小写
|
|
117
|
+
*/
|
|
118
|
+
arrOptions,
|
|
119
|
+
} = params || {};
|
|
120
|
+
this._arrOptions = arrOptions;
|
|
110
121
|
}
|
|
111
122
|
|
|
112
123
|
/**
|
|
@@ -123,7 +134,7 @@ class ArrayDataModel extends ArrayUtils {
|
|
|
123
134
|
}
|
|
124
135
|
|
|
125
136
|
return new Promise((resolve, reject) => {
|
|
126
|
-
let res = this.findItem(query);
|
|
137
|
+
let res = this.findItem(query, this._arrOptions);
|
|
127
138
|
if (this.getMap) {
|
|
128
139
|
res = this.getMap(res);
|
|
129
140
|
}
|
|
@@ -151,7 +162,7 @@ class ArrayDataModel extends ArrayUtils {
|
|
|
151
162
|
if (this.getListFunc) {
|
|
152
163
|
resultList = await this.getListFunc(query);
|
|
153
164
|
} else {
|
|
154
|
-
const getPro = this.findListByQuery(query);
|
|
165
|
+
const getPro = this.findListByQuery(query, this._arrOptions);
|
|
155
166
|
resultList = await getPro;
|
|
156
167
|
}
|
|
157
168
|
if (this.getListMap) {
|