mm_store 0.0.0-stage → 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 qww
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,195 @@
1
- # Temporary Holding Version
1
+ # 存储管理器(mm_store)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ ## 说明
4
+ mm_store 是基于内存列表的存储类,提供统一的增删改查(CRUD)、分页、关键词搜索与排序能力。
5
+ 通过配置主键、名称字段、分页大小、过滤参数名与模糊匹配,可快速搭建内存数据管理。
6
+
7
+ ## 功能特点
8
+
9
+ - 支持增删改查:`add` / `del` / `delById` / `delByName` / `set` / `setById` / `setByName`
10
+ - 支持精确与模糊(like)查询匹配
11
+ - 支持分页与关键词搜索
12
+ - 支持按字段排序
13
+ - 支持清空与整体设置列表
14
+ - 支持自定义主键、名称字段、分页大小等配置
15
+ - 基于 mm_corejs 的 Admin 管理器,支持按作用域缓存实例
16
+
17
+ ## 安装
18
+
19
+ ```bash
20
+ npm install mm_store
21
+ ```
22
+
23
+ ## 快速开始
24
+
25
+ ```javascript
26
+ const { Store, storeAdmin } = require('mm_store');
27
+
28
+ // 直接使用存储类
29
+ const store = new Store();
30
+ store.add({ id: 1, name: '苹果', type: 'fruit' });
31
+ store.add({ id: 2, name: '香蕉', type: 'fruit' });
32
+ console.log(store.list({ type: 'fruit' }));
33
+
34
+ // 通过管理器按作用域获取实例(相同作用域返回同一实例)
35
+ const scoped = storeAdmin('app');
36
+ scoped.add({ id: 1, name: '配置项' });
37
+ ```
38
+
39
+ ## API
40
+
41
+ ### 类方法
42
+
43
+ | 方法 | 说明 | 参数 | 返回值 |
44
+ |---|---|---|---|
45
+ | add | 添加数据项 | item | void |
46
+ | del | 按查询条件删除 | query, all | boolean |
47
+ | delById | 按主键删除 | id, all | boolean |
48
+ | delByName | 按名称删除 | name, all | boolean |
49
+ | set | 按查询条件更新 | query, item, all | boolean |
50
+ | setById | 按主键更新 | id, item, all | boolean |
51
+ | setByName | 按名称更新 | name, item, all | boolean |
52
+ | get | 按查询条件获取单项 | query | object |
53
+ | getById | 按主键获取 | id | object |
54
+ | getByName | 按名称获取 | name | object |
55
+ | list | 获取列表,支持分页与关键词 | query | Array |
56
+ | count | 获取匹配数量 | query | number |
57
+ | clear | 清空列表 | - | void |
58
+ | setList | 整体设置列表 | list | void |
59
+ | sort | 按字段排序 | field, order | void |
60
+
61
+ ### 工厂函数
62
+
63
+ | 函数 | 说明 | 参数 | 返回值 |
64
+ |---|---|---|---|
65
+ | storeAdmin | 获取指定作用域的存储实例,不存在时创建 | scope, config | Store |
66
+ | getStore | 获取 os 作用域的存储实例 | - | Store |
67
+
68
+ ## 配置项
69
+
70
+ | 配置 | 类型 | 默认值 | 说明 |
71
+ |---|---|---|---|
72
+ | key | string | 'id' | 主键字段 |
73
+ | name | string | 'name' | 名称字段 |
74
+ | size | number | 10 | 默认分页大小 |
75
+ | filter.page_now | string | 'page_now' | 分页页码参数名 |
76
+ | filter.page_size | string | 'page_size' | 分页大小参数名 |
77
+ | filter.orderby | string | 'orderby' | 排序字段参数名 |
78
+ | filter.groupby | string | 'groupby' | 分组字段参数名 |
79
+ | filter.keyword | string | 'keyword' | 搜索关键词参数名 |
80
+ | keyword | string[] | ['name', 'title', 'description'] | 关键词搜索字段 |
81
+ | like | boolean | false | 是否启用模糊匹配 |
82
+
83
+ ## 使用示例
84
+
85
+ ### 1. 基本增删改查
86
+
87
+ ```javascript
88
+ const { Store } = require('mm_store');
89
+
90
+ const store = new Store();
91
+ store.add({ id: 1, name: '张三', age: 18 });
92
+
93
+ // 查询
94
+ console.log(store.getById(1)); // { id: 1, name: '张三', age: 18 }
95
+ console.log(store.getByName('张三')); // { id: 1, name: '张三', age: 18 }
96
+
97
+ // 更新(保留原主键)
98
+ store.setById(1, { age: 20 });
99
+ console.log(store.getById(1)); // { id: 1, name: '张三', age: 20 }
100
+
101
+ // 删除
102
+ console.log(store.delById(1)); // true
103
+ console.log(store.delById(1)); // false
104
+ ```
105
+
106
+ ### 2. 分页与关键词搜索
107
+
108
+ ```javascript
109
+ const { Store } = require('mm_store');
110
+
111
+ const store = new Store();
112
+ store.add({ id: 1, name: '苹果', type: 'fruit' });
113
+ store.add({ id: 2, name: '香蕉', type: 'fruit' });
114
+ store.add({ id: 3, name: '白菜', type: 'vegetable' });
115
+
116
+ // 条件过滤
117
+ console.log(store.list({ type: 'fruit' })); // 苹果、香蕉
118
+
119
+ // 关键词搜索(在 name / title / description 字段中匹配)
120
+ console.log(store.list({ keyword: '蕉' })); // 香蕉
121
+
122
+ // 分页
123
+ console.log(store.list({ page_now: 1, page_size: 2 })); // 苹果、香蕉
124
+ console.log(store.count({ type: 'fruit' })); // 2
125
+ ```
126
+
127
+ ### 3. 排序
128
+
129
+ ```javascript
130
+ const { Store } = require('mm_store');
131
+
132
+ const store = new Store();
133
+ store.add({ id: 1, name: 'A' });
134
+ store.add({ id: 3, name: 'C' });
135
+ store.add({ id: 2, name: 'B' });
136
+
137
+ store.sort('id', 'desc');
138
+ console.log(store.list()); // 按 id 降序
139
+ ```
140
+
141
+ ### 4. 自定义配置
142
+
143
+ ```javascript
144
+ const { Store } = require('mm_store');
145
+
146
+ const store = new Store({
147
+ key: 'code', // 主键字段
148
+ name: 'title', // 名称字段
149
+ size: 5, // 分页大小
150
+ like: true, // 启用模糊匹配
151
+ keyword: ['title'] // 关键词搜索字段
152
+ });
153
+
154
+ store.add({ code: 'A1', title: 'Hello World' });
155
+ console.log(store.get({ title: 'World' })); // 模糊匹配到 A1
156
+ ```
157
+
158
+ ### 5. 作用域工厂
159
+
160
+ ```javascript
161
+ const { storeAdmin, getStore } = require('mm_store');
162
+
163
+ // 按作用域获取,相同作用域返回同一实例
164
+ const app = storeAdmin('app');
165
+ app.add({ id: 1, name: '配置项A' });
166
+
167
+ const appAgain = storeAdmin('app');
168
+ console.log(appAgain === app); // true
169
+
170
+ // 获取默认 os 作用域实例
171
+ const osStore = getStore();
172
+ osStore.add({ id: 1, name: '全局项' });
173
+ ```
174
+
175
+ ## 注意事项
176
+
177
+ 1. `add` 时主键与名称字段不能为空,主键不能重复。
178
+ 2. `set` / `setById` / `setByName` 更新时强制保留原主键。
179
+ 3. `list` 未传 `page_now` 时返回全部匹配项,传入后才分页。
180
+ 4. `del` 默认只删除第一个匹配项,传 `all = true` 删除全部匹配项。
181
+ 5. 通过 `storeAdmin(scope, config)` 传入的配置仅首次创建时生效,同作用域后续调用复用已有实例。
182
+
183
+ ## 依赖
184
+
185
+ - mm_corejs:提供 Admin 管理器
186
+
187
+ ## 测试
188
+
189
+ ```bash
190
+ npm test
191
+ ```
192
+
193
+ ## 许可证
194
+
195
+ ISC
@@ -0,0 +1,45 @@
1
+ /**
2
+ * mm_store 存储类示例
3
+ * 演示 Store 类的增删改查、分页、关键词搜索与排序,
4
+ * 以及 storeAdmin() 作用域工厂与 getStore() 的用法。
5
+ * 运行:node example/index.js
6
+ */
7
+ const { Store, storeAdmin, getStore } = require('../index.js');
8
+
9
+ /**
10
+ * 主流程
11
+ */
12
+ function main() {
13
+ // 方式1:类实例
14
+ const store = new Store();
15
+ store.add({ id: 1, name: '苹果', type: 'fruit' });
16
+ store.add({ id: 2, name: '香蕉', type: 'fruit' });
17
+ store.add({ id: 3, name: '白菜', type: 'vegetable' });
18
+ console.log('全部列表:', store.list());
19
+ console.log('水果列表:', store.list({ type: 'fruit' }));
20
+ console.log('关键词"蕉":', store.list({ keyword: '蕉' }));
21
+ console.log('第一页:', store.list({ page_now: 1, page_size: 2 }));
22
+
23
+ // 更新(保留原主键)
24
+ store.setById(1, { name: '红苹果' });
25
+ console.log('更新后 id=1:', store.getById(1));
26
+
27
+ // 删除
28
+ console.log('删除 id=3:', store.delById(3));
29
+
30
+ // 排序
31
+ store.sort('id', 'desc');
32
+ console.log('按 id 降序:', store.list());
33
+
34
+ // 方式2:作用域工厂(相同作用域返回同一实例)
35
+ const scoped = storeAdmin('app');
36
+ scoped.add({ id: 1, name: '配置项A' });
37
+ console.log('工厂实例:', scoped.list());
38
+
39
+ // 方式3:获取 os 作用域实例
40
+ const os_store = getStore();
41
+ os_store.add({ id: 1, name: '全局项' });
42
+ console.log('os 作用域:', os_store.list());
43
+ }
44
+
45
+ main();
package/index.js ADDED
@@ -0,0 +1,36 @@
1
+ const Admin = require('mm_corejs').Admin;
2
+ const Store = require('./lib/store');
3
+
4
+ const admin = new Admin();
5
+ admin.setClass(Store);
6
+
7
+ /**
8
+ * 获取指定作用域的存储实例,不存在时创建
9
+ * @param {string} [scope] 作用域名称,缺省时使用 os
10
+ * @param {Object} [config] 存储配置项
11
+ * @returns {Store} 存储实例
12
+ */
13
+ function storeAdmin(scope, config) {
14
+ if (!scope) {
15
+ scope = 'os';
16
+ }
17
+ let store = admin.get(scope, config);
18
+ if (!store) {
19
+ store = new Store(config);
20
+ admin.set(scope, store);
21
+ }
22
+ return store;
23
+ }
24
+
25
+ module.exports = {
26
+ Store,
27
+ admin,
28
+ storeAdmin,
29
+ /**
30
+ * 获取 os 作用域的存储实例
31
+ * @returns {Store} 存储实例
32
+ */
33
+ getStore() {
34
+ return storeAdmin('os');
35
+ }
36
+ };
package/lib/store.js ADDED
@@ -0,0 +1,440 @@
1
+ /**
2
+ * 存储类
3
+ */
4
+ class Store {
5
+ // 存储列表
6
+ #list = [];
7
+
8
+ /**
9
+ * 配置项
10
+ */
11
+ static config = {
12
+ // 主键
13
+ key: 'id',
14
+ // 名称字段
15
+ name: 'name',
16
+ // 默认分页大小
17
+ size: 10,
18
+ // 过滤器配置 - 用于过滤query参数
19
+ filter: {
20
+ // 分页码
21
+ page_now: 'page_now',
22
+ // 分页大小
23
+ page_size: 'page_size',
24
+ // 排序字段
25
+ orderby: 'orderby',
26
+ // 分组字段
27
+ groupby: 'groupby',
28
+ // 搜索关键词
29
+ keyword: 'keyword'
30
+ },
31
+ // 关键词查询字段
32
+ keyword: ['name', 'title', 'description'],
33
+ // 模糊查询字段
34
+ like: false
35
+ };
36
+
37
+ /**
38
+ * 构造函数
39
+ * @param {Object} config 配置项
40
+ */
41
+ constructor(config) {
42
+ this.setConfig(config);
43
+ }
44
+
45
+ /**
46
+ * 设置配置项
47
+ * @param {Object} config 配置项
48
+ * @returns {void}
49
+ */
50
+ setConfig(config) {
51
+ this.config = {
52
+ ...Store.config,
53
+ ...this.config,
54
+ ...config,
55
+ filter: {
56
+ ...Store.config.filter,
57
+ ...(this.config && this.config.filter),
58
+ ...(config && config.filter)
59
+ }
60
+ };
61
+ }
62
+
63
+ /**
64
+ * 匹配查询项
65
+ * @param {Object} obj 数据项
66
+ * @param {Object} qy 查询项
67
+ * @returns {boolean} 是否匹配成功,成功返回true,失败返回false
68
+ */
69
+ match(obj, qy) {
70
+ let like = this.config.like;
71
+ return Object.keys(qy).every(field => {
72
+ let is_string = typeof obj[field] === 'string';
73
+ if (like && is_string) {
74
+ return String(obj[field]).includes(String(qy[field] ?? ''));
75
+ }
76
+ return obj[field] === qy[field];
77
+ });
78
+ }
79
+
80
+ /**
81
+ * 判断值是否为空
82
+ * @param {*} value 待判断的值
83
+ * @returns {boolean} undefined、null、空字符串返回true,其余返回false
84
+ */
85
+ _isEmpty(value) {
86
+ return value === undefined || value === null || value === '';
87
+ }
88
+
89
+ /**
90
+ * 断言项是否符合要求
91
+ * @param {Object} item 要断言的项
92
+ * @returns {void}
93
+ * @throws {Error} 主键不能为空
94
+ */
95
+ _assertKey(item) {
96
+ if (this._isEmpty(item[this.config.key])) {
97
+ throw new Error('主键不能为空');
98
+ }
99
+ }
100
+
101
+ /**
102
+ * 断言项是否符合要求
103
+ * @param {Object} item 要断言的项
104
+ * @returns {void}
105
+ * @throws {Error} 名称不能为空
106
+ */
107
+ _assertName(item) {
108
+ if (this._isEmpty(item[this.config.name])) {
109
+ throw new Error('名称不能为空');
110
+ }
111
+ }
112
+
113
+ /**
114
+ * 断言项是否符合要求
115
+ * @param {Object} item 要断言的项
116
+ * @returns {void}
117
+ * @throws {Error} 主键已存在
118
+ */
119
+ _assertHas(item) {
120
+ if (this.getById(item[this.config.key])) {
121
+ throw new Error('主键已存在');
122
+ }
123
+ }
124
+
125
+ /**
126
+ * 添加项
127
+ * @param {Object} item 要添加的项
128
+ * @returns {void}
129
+ */
130
+ add(item) {
131
+ this._assertKey(item);
132
+ this._assertName(item);
133
+ this._assertHas(item);
134
+ this.#list.push(item);
135
+ }
136
+
137
+ /**
138
+ * 删除项
139
+ * @param {Object} query 查询条件,符合条件的删除
140
+ * @param {boolean} all 是否删除所有符合条件的项,默认false(只删第一个匹配项)
141
+ * @returns {boolean} 是否删除了数据项,删除成功返回true,否则返回false
142
+ */
143
+ del(query, all = false) {
144
+ if (!all) {
145
+ const index = this.#list.findIndex(o => this.match(o, query));
146
+ if (index < 0) return false;
147
+ this.#list.splice(index, 1);
148
+ return true;
149
+ }
150
+ let deleted = false;
151
+ // 倒叙遍历,避免索引变化导致的跳过
152
+ for (let i = this.#list.length - 1; i >= 0; i--) {
153
+ if (this.match(this.#list[i], query)) {
154
+ this.#list.splice(i, 1);
155
+ deleted = true;
156
+ }
157
+ }
158
+ return deleted;
159
+ }
160
+
161
+ /**
162
+ * 删除项 - 根据主键删除
163
+ * @param {string} id 主键值
164
+ * @param {boolean} all 是否删除所有符合条件的项,默认false(只删第一个匹配项)
165
+ * @returns {boolean} 是否删除了数据项,删除成功返回true,否则返回false
166
+ */
167
+ delById(id, all = false) {
168
+ if (!all) {
169
+ const index = this.#list.findIndex(o => o[this.config.key] === id);
170
+ if (index < 0) return false;
171
+ this.#list.splice(index, 1);
172
+ return true;
173
+ }
174
+ let deleted = false;
175
+ // 倒叙遍历,避免索引变化导致的跳过
176
+ for (let i = this.#list.length - 1; i >= 0; i--) {
177
+ if (this.#list[i][this.config.key] === id) {
178
+ this.#list.splice(i, 1);
179
+ deleted = true;
180
+ }
181
+ }
182
+ return deleted;
183
+ }
184
+
185
+ /**
186
+ * 删除项 - 根据名称删除
187
+ * @param {string} name 名称值
188
+ * @param {boolean} all 是否删除所有符合条件的项,默认false(只删第一个匹配项)
189
+ * @returns {boolean} 是否删除了数据项,删除成功返回true,否则返回false
190
+ */
191
+ delByName(name, all = false) {
192
+ if (!all) {
193
+ const index = this.#list.findIndex(o => o[this.config.name] === name);
194
+ if (index < 0) return false;
195
+ this.#list.splice(index, 1);
196
+ return true;
197
+ }
198
+ let deleted = false;
199
+ // 倒叙遍历,避免索引变化导致的跳过
200
+ for (let i = this.#list.length - 1; i >= 0; i--) {
201
+ if (this.#list[i][this.config.name] === name) {
202
+ this.#list.splice(i, 1);
203
+ deleted = true;
204
+ }
205
+ }
206
+ return deleted;
207
+ }
208
+
209
+ /**
210
+ * 设置查询项
211
+ * @param {Object} query 查询项
212
+ * @param {Object} item 要设置的项
213
+ * @param {boolean} all 是否设置所有符合条件的项,默认false(只设第一个匹配项)
214
+ * @returns {boolean} 是否设置了数据项,设置成功返回true,否则返回false
215
+ */
216
+ set(query, item, all = false) {
217
+ if (!all) {
218
+ const index = this.#list.findIndex(o => this.match(o, query));
219
+ if (index < 0) return false;
220
+ // 合并更新,并强制保留原主键
221
+ const id = this.#list[index][this.config.key];
222
+ this.#list[index] = { ...this.#list[index], ...item, [this.config.key]: id };
223
+ return true;
224
+ }
225
+ let changed = false;
226
+ for (let i = 0; i < this.#list.length; i++) {
227
+ if (this.match(this.#list[i], query)) {
228
+ // 合并更新,并强制保留原主键
229
+ const id = this.#list[i][this.config.key];
230
+ this.#list[i] = { ...this.#list[i], ...item, [this.config.key]: id };
231
+ changed = true;
232
+ }
233
+ }
234
+ return changed;
235
+ }
236
+
237
+ /**
238
+ * 设置查询项 - 根据主键设置
239
+ * @param {string} id 主键值
240
+ * @param {Object} item 要设置的项
241
+ * @param {boolean} all 是否设置所有符合条件的项,默认false(只设第一个匹配项)
242
+ * @returns {boolean} 是否设置了数据项,设置成功返回true,否则返回false
243
+ */
244
+ setById(id, item, all = false) {
245
+ if (!all) {
246
+ const index = this.#list.findIndex(o => o[this.config.key] === id);
247
+ if (index < 0) return false;
248
+ // 合并更新,并强制保留原主键
249
+ this.#list[index] = { ...this.#list[index], ...item, [this.config.key]: id };
250
+ return true;
251
+ }
252
+ let changed = false;
253
+ for (let i = 0; i < this.#list.length; i++) {
254
+ if (this.#list[i][this.config.key] === id) {
255
+ // 合并更新,并强制保留原主键
256
+ this.#list[i] = { ...this.#list[i], ...item, [this.config.key]: id };
257
+ changed = true;
258
+ }
259
+ }
260
+ return changed;
261
+ }
262
+
263
+ /**
264
+ * 设置查询项 - 根据名称设置
265
+ * @param {string} name 名称值
266
+ * @param {Object} item 要设置的项
267
+ * @param {boolean} all 是否设置所有符合条件的项,默认false(只设第一个匹配项)
268
+ * @returns {boolean} 是否设置了数据项,设置成功返回true,否则返回false
269
+ */
270
+ setByName(name, item, all = false) {
271
+ if (!all) {
272
+ const index = this.#list.findIndex(o => o[this.config.name] === name);
273
+ if (index < 0) return false;
274
+ // 合并更新,并强制保留原主键
275
+ const id = this.#list[index][this.config.key];
276
+ this.#list[index] = { ...this.#list[index], ...item, [this.config.key]: id };
277
+ return true;
278
+ }
279
+ let changed = false;
280
+ for (let i = 0; i < this.#list.length; i++) {
281
+ if (this.#list[i][this.config.name] === name) {
282
+ // 合并更新,并强制保留原主键
283
+ const id = this.#list[i][this.config.key];
284
+ this.#list[i] = { ...this.#list[i], ...item, [this.config.key]: id };
285
+ changed = true;
286
+ }
287
+ }
288
+ return changed;
289
+ }
290
+
291
+ /**
292
+ * 获取查询项 - 单项数据
293
+ * @param {Object} query 查询项
294
+ * @returns {Object} 匹配到的数据项
295
+ */
296
+ get(query) {
297
+ return this.#list.find(o => this.match(o, query));
298
+ }
299
+
300
+ /**
301
+ * 获取查询项 - 根据主键获取
302
+ * @param {string} id 主键值
303
+ * @returns {Object} 匹配到的数据项
304
+ */
305
+ getById(id) {
306
+ return this.#list.find(o => o[this.config.key] === id);
307
+ }
308
+
309
+ /**
310
+ * 获取查询项 - 根据名称获取
311
+ * @param {string} name 名称值
312
+ * @returns {Object} 匹配到的数据项
313
+ */
314
+ getByName(name) {
315
+ return this.#list.find(o => o[this.config.name] === name);
316
+ }
317
+
318
+ /**
319
+ * 过滤列表
320
+ * @param {Object} query 查询项
321
+ * @returns {Array} 过滤后的列表
322
+ */
323
+ _matchList(query) {
324
+ return this.#list.filter(o => this.match(o, query));
325
+ }
326
+
327
+ /**
328
+ * 搜索列表 - 关键词模糊搜索
329
+ * @param {Object} query 查询项
330
+ * @param {string} keyword 搜索关键词
331
+ * @returns {Array} 搜索后的列表
332
+ */
333
+ _searchList(query, keyword) {
334
+ const list = this._matchList(query);
335
+ if (!keyword) {
336
+ return list;
337
+ }
338
+ const fields = this.config.keyword || [];
339
+ return list.filter(o =>
340
+ fields.some(field => String(o[field] ?? '').includes(String(keyword)))
341
+ );
342
+ }
343
+
344
+ /**
345
+ * 获取列表 - 多项数据
346
+ * @param {Object} query 查询项
347
+ * @returns {Array} 匹配的数据列表
348
+ */
349
+ list(query) {
350
+ if (!query) {
351
+ return [...this.#list];
352
+ }
353
+ const page_key = this.config.filter.page_now;
354
+ const size_key = this.config.filter.page_size;
355
+ const keyword_key = this.config.filter.keyword;
356
+ const qy = { ...query };
357
+ const page = qy[page_key] || 1;
358
+ const size = qy[size_key] || this.config.size;
359
+ const keyword = qy[keyword_key];
360
+ delete qy[page_key];
361
+ delete qy[size_key];
362
+ delete qy[this.config.filter.orderby];
363
+ delete qy[this.config.filter.groupby];
364
+ delete qy[keyword_key];
365
+
366
+ const filtered = this._searchList(qy, keyword);
367
+ if (query[page_key]) {
368
+ const offset = (page - 1) * size;
369
+ return filtered.slice(offset, offset + size);
370
+ }
371
+ return filtered;
372
+ }
373
+
374
+ /**
375
+ * 获取列表大小 - 多项数据
376
+ * @param {Object} query 查询项
377
+ * @returns {number} 匹配的总数
378
+ */
379
+ count(query) {
380
+ if (!query) {
381
+ return this.#list.length;
382
+ }
383
+ const page_key = this.config.filter.page_now;
384
+ const size_key = this.config.filter.page_size;
385
+ const keyword_key = this.config.filter.keyword;
386
+ const qy = { ...query };
387
+ const keyword = qy[keyword_key];
388
+ delete qy[page_key];
389
+ delete qy[size_key];
390
+ delete qy[this.config.filter.orderby];
391
+ delete qy[this.config.filter.groupby];
392
+ delete qy[keyword_key];
393
+ return this._searchList(qy, keyword).length;
394
+ }
395
+
396
+ /**
397
+ * 清空列表
398
+ * @returns {void}
399
+ */
400
+ clear() {
401
+ this.#list = [];
402
+ }
403
+
404
+ /**
405
+ * 设置列表
406
+ * @param {Array} list 要设置的列表
407
+ * @returns {void}
408
+ */
409
+ setList(list) {
410
+ this.#list = list;
411
+ }
412
+
413
+ /**
414
+ * 排序列表 - 按指定字段排序
415
+ * @param {string} orderby 排序字段,默认'sort'字段
416
+ * @param {string} order 排序方向,默认'asc'
417
+ * @returns {void}
418
+ */
419
+ sort(field = 'sort', order = 'asc') {
420
+ if (order !== 'asc' && order !== 'desc') {
421
+ order = 'asc';
422
+ }
423
+ if (order === 'asc') {
424
+ this.#list.sort((a, b) => {
425
+ const val_a = a[field];
426
+ const val_b = b[field];
427
+ return val_a < val_b ? -1 : val_a > val_b ? 1 : 0;
428
+ });
429
+ }
430
+ else {
431
+ this.#list.sort((a, b) => {
432
+ const val_a = a[field];
433
+ const val_b = b[field];
434
+ return val_b < val_a ? -1 : val_b > val_a ? 1 : 0;
435
+ });
436
+ }
437
+ }
438
+ }
439
+
440
+ module.exports = Store;
package/package.json CHANGED
@@ -1,6 +1,37 @@
1
1
  {
2
2
  "name": "mm_store",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.0.0",
4
+ "description": "存储类:提供基于内存列表的增删改查、分页、关键词搜索与排序能力,支持自定义主键与过滤配置",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "node test.js"
8
+ },
9
+ "keywords": [
10
+ "mm_mod",
11
+ "mm_infra",
12
+ "mm_data",
13
+ "store",
14
+ "存储",
15
+ "列表",
16
+ "分页",
17
+ "搜索",
18
+ "排序"
19
+ ],
20
+ "layer": "infra",
21
+ "group": "data",
22
+ "author": "邱文武",
23
+ "license": "ISC",
24
+ "dependencies": {
25
+ "mm_corejs": "^1.1.4"
26
+ },
27
+ "engines": {
28
+ "node": ">=14.0.0"
29
+ },
30
+ "files": [
31
+ "index.js",
32
+ "lib",
33
+ "example",
34
+ "README.md",
35
+ "LICENSE"
36
+ ]
6
37
  }