@fcurd/core 0.1.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/dist/index.d.mts +337 -0
- package/dist/index.d.ts +337 -0
- package/dist/index.js +719 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +702 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +37 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var vue = require('vue');
|
|
4
|
+
|
|
5
|
+
// src/hooks/useCrudActions.ts
|
|
6
|
+
function useCrudActions(options = {}) {
|
|
7
|
+
const { actions: initialActions = [] } = options;
|
|
8
|
+
const actions = vue.ref([...initialActions]);
|
|
9
|
+
function getByArea(area) {
|
|
10
|
+
return actions.value.filter((a) => a.area === area).sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
11
|
+
}
|
|
12
|
+
function register(action) {
|
|
13
|
+
const existing = actions.value.findIndex((a) => a.id === action.id);
|
|
14
|
+
if (existing >= 0) {
|
|
15
|
+
actions.value[existing] = action;
|
|
16
|
+
} else {
|
|
17
|
+
actions.value.push(action);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function unregister(id) {
|
|
21
|
+
const idx = actions.value.findIndex((a) => a.id === id);
|
|
22
|
+
if (idx >= 0) {
|
|
23
|
+
actions.value.splice(idx, 1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
actions,
|
|
28
|
+
getByArea,
|
|
29
|
+
register,
|
|
30
|
+
unregister
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function createAction(options) {
|
|
34
|
+
return {
|
|
35
|
+
id: "create",
|
|
36
|
+
label: options.label ?? "\u65B0\u589E",
|
|
37
|
+
type: "primary",
|
|
38
|
+
area: "toolbar",
|
|
39
|
+
order: 0,
|
|
40
|
+
onClick: options.onClick
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function editAction(options) {
|
|
44
|
+
return {
|
|
45
|
+
id: "edit",
|
|
46
|
+
label: options.label ?? "\u7F16\u8F91",
|
|
47
|
+
type: "default",
|
|
48
|
+
area: "row",
|
|
49
|
+
order: 0,
|
|
50
|
+
onClick: (ctx) => {
|
|
51
|
+
if (ctx.row) {
|
|
52
|
+
options.onClick(ctx.row);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function deleteAction(options) {
|
|
58
|
+
const { adapter, getId = (row) => row?.id, confirm = true } = options;
|
|
59
|
+
return {
|
|
60
|
+
id: "delete",
|
|
61
|
+
label: options.label ?? "\u5220\u9664",
|
|
62
|
+
type: "error",
|
|
63
|
+
area: "row",
|
|
64
|
+
order: 10,
|
|
65
|
+
confirm: confirm === true ? "\u786E\u5B9A\u8981\u5220\u9664\u6B64\u8BB0\u5F55\u5417\uFF1F" : confirm,
|
|
66
|
+
onClick: async (ctx) => {
|
|
67
|
+
if (!ctx.row || !adapter.remove)
|
|
68
|
+
return;
|
|
69
|
+
try {
|
|
70
|
+
const id = getId(ctx.row);
|
|
71
|
+
await adapter.remove(id);
|
|
72
|
+
await ctx.refresh();
|
|
73
|
+
options.onSuccess?.();
|
|
74
|
+
} catch (err) {
|
|
75
|
+
options.onError?.(err);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function batchDeleteAction(options) {
|
|
81
|
+
const { adapter, getId = (row) => row?.id, confirm = true } = options;
|
|
82
|
+
return {
|
|
83
|
+
id: "batchDelete",
|
|
84
|
+
label: options.label ?? "\u6279\u91CF\u5220\u9664",
|
|
85
|
+
type: "error",
|
|
86
|
+
area: "batch",
|
|
87
|
+
order: 0,
|
|
88
|
+
confirm: confirm === true ? "\u786E\u5B9A\u8981\u5220\u9664\u9009\u4E2D\u7684\u8BB0\u5F55\u5417\uFF1F" : confirm,
|
|
89
|
+
visible: (ctx) => ctx.selectedIds.length > 0,
|
|
90
|
+
onClick: async (ctx) => {
|
|
91
|
+
if (!adapter.remove || ctx.selectedIds.length === 0)
|
|
92
|
+
return;
|
|
93
|
+
try {
|
|
94
|
+
await Promise.all(ctx.selectedIds.map((id) => adapter.remove(id)));
|
|
95
|
+
ctx.clearSelection();
|
|
96
|
+
await ctx.refresh();
|
|
97
|
+
options.onSuccess?.();
|
|
98
|
+
} catch (err) {
|
|
99
|
+
options.onError?.(err);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function exportAction(options) {
|
|
105
|
+
const { adapter } = options;
|
|
106
|
+
return {
|
|
107
|
+
id: "export",
|
|
108
|
+
label: options.label ?? "\u5BFC\u51FA",
|
|
109
|
+
type: "default",
|
|
110
|
+
area: "toolbar",
|
|
111
|
+
order: 100,
|
|
112
|
+
visible: () => typeof adapter.export === "function",
|
|
113
|
+
onClick: async (ctx) => {
|
|
114
|
+
if (!adapter.export)
|
|
115
|
+
return;
|
|
116
|
+
try {
|
|
117
|
+
const query = ctx.query ?? {};
|
|
118
|
+
const sort = ctx.sort ?? null;
|
|
119
|
+
const result = await adapter.export({
|
|
120
|
+
query,
|
|
121
|
+
sort
|
|
122
|
+
});
|
|
123
|
+
options.handleExport?.(result, options.filename);
|
|
124
|
+
options.onSuccess?.();
|
|
125
|
+
} catch (err) {
|
|
126
|
+
options.onError?.(err);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
var presetActions = {
|
|
132
|
+
create: createAction,
|
|
133
|
+
edit: editAction,
|
|
134
|
+
delete: deleteAction,
|
|
135
|
+
batchDelete: batchDeleteAction,
|
|
136
|
+
export: exportAction
|
|
137
|
+
};
|
|
138
|
+
function useCrudForm(options) {
|
|
139
|
+
const { fields, initialData } = options;
|
|
140
|
+
const model = vue.reactive({});
|
|
141
|
+
const mode = vue.ref("create");
|
|
142
|
+
const initialSnapshot = vue.ref({});
|
|
143
|
+
function initModel(data) {
|
|
144
|
+
Object.keys(model).forEach((key) => {
|
|
145
|
+
delete model[key];
|
|
146
|
+
});
|
|
147
|
+
if (data) {
|
|
148
|
+
Object.assign(model, data);
|
|
149
|
+
}
|
|
150
|
+
initialSnapshot.value = { ...model };
|
|
151
|
+
}
|
|
152
|
+
if (initialData) {
|
|
153
|
+
initModel(initialData);
|
|
154
|
+
}
|
|
155
|
+
const changedKeys = vue.computed(() => {
|
|
156
|
+
const before = initialSnapshot.value ?? {};
|
|
157
|
+
const after = model;
|
|
158
|
+
const keys = /* @__PURE__ */ new Set([
|
|
159
|
+
...Object.keys(before),
|
|
160
|
+
...Object.keys(after)
|
|
161
|
+
]);
|
|
162
|
+
const changed = [];
|
|
163
|
+
keys.forEach((key) => {
|
|
164
|
+
const a = before[key];
|
|
165
|
+
const b = after[key];
|
|
166
|
+
if (a !== b)
|
|
167
|
+
changed.push(key);
|
|
168
|
+
});
|
|
169
|
+
return changed;
|
|
170
|
+
});
|
|
171
|
+
const changedData = vue.computed(() => {
|
|
172
|
+
const after = model;
|
|
173
|
+
const data = {};
|
|
174
|
+
for (const key of changedKeys.value)
|
|
175
|
+
data[key] = after[key];
|
|
176
|
+
return data;
|
|
177
|
+
});
|
|
178
|
+
const dirty = vue.computed(() => changedKeys.value.length > 0);
|
|
179
|
+
const visibleFields = vue.computed(() => {
|
|
180
|
+
return fields.filter((field) => {
|
|
181
|
+
const formVisible = field.visibleIn?.form;
|
|
182
|
+
if (formVisible === void 0 || formVisible === true)
|
|
183
|
+
return true;
|
|
184
|
+
if (formVisible === false)
|
|
185
|
+
return false;
|
|
186
|
+
if (typeof formVisible === "function") {
|
|
187
|
+
const ctx = {
|
|
188
|
+
surface: "form",
|
|
189
|
+
formModel: model
|
|
190
|
+
};
|
|
191
|
+
return formVisible(ctx);
|
|
192
|
+
}
|
|
193
|
+
return true;
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
function reset(data) {
|
|
197
|
+
initModel(data);
|
|
198
|
+
}
|
|
199
|
+
function setMode(newMode) {
|
|
200
|
+
mode.value = newMode;
|
|
201
|
+
}
|
|
202
|
+
function getSubmitData() {
|
|
203
|
+
if (mode.value === "create") {
|
|
204
|
+
return { ...model };
|
|
205
|
+
}
|
|
206
|
+
return changedData.value;
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
model,
|
|
210
|
+
mode,
|
|
211
|
+
dirty,
|
|
212
|
+
changedKeys,
|
|
213
|
+
changedData,
|
|
214
|
+
visibleFields,
|
|
215
|
+
reset,
|
|
216
|
+
setMode,
|
|
217
|
+
getSubmitData
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function useCrudList(options) {
|
|
221
|
+
const {
|
|
222
|
+
adapter,
|
|
223
|
+
initialQuery,
|
|
224
|
+
initialPage = 1,
|
|
225
|
+
initialPageSize = 20,
|
|
226
|
+
autoFetch = true,
|
|
227
|
+
debounceMs = 0,
|
|
228
|
+
dedupe = true,
|
|
229
|
+
onError
|
|
230
|
+
} = options;
|
|
231
|
+
const rows = vue.shallowRef([]);
|
|
232
|
+
const total = vue.ref(0);
|
|
233
|
+
const loading = vue.ref(false);
|
|
234
|
+
const error = vue.ref(null);
|
|
235
|
+
const query = vue.shallowRef(initialQuery ?? {});
|
|
236
|
+
const sort = vue.shallowRef(null);
|
|
237
|
+
const page = vue.ref(initialPage);
|
|
238
|
+
const pageSize = vue.ref(initialPageSize);
|
|
239
|
+
let fetchTimer = null;
|
|
240
|
+
let requestSeq = 0;
|
|
241
|
+
let activeSeq = 0;
|
|
242
|
+
let lastKey = null;
|
|
243
|
+
let activeKey = null;
|
|
244
|
+
let abortController = null;
|
|
245
|
+
function buildKey() {
|
|
246
|
+
try {
|
|
247
|
+
return JSON.stringify({
|
|
248
|
+
page: page.value,
|
|
249
|
+
pageSize: pageSize.value,
|
|
250
|
+
query: query.value,
|
|
251
|
+
sort: sort.value
|
|
252
|
+
});
|
|
253
|
+
} catch {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function isAbortError(err) {
|
|
258
|
+
if (!err || typeof err !== "object")
|
|
259
|
+
return false;
|
|
260
|
+
const obj = err;
|
|
261
|
+
return obj.name === "AbortError" || obj.code === "ABORT_ERR";
|
|
262
|
+
}
|
|
263
|
+
function isPlainObject(value) {
|
|
264
|
+
return Object.prototype.toString.call(value) === "[object Object]";
|
|
265
|
+
}
|
|
266
|
+
function pruneEmptyDeep(value) {
|
|
267
|
+
if (value === void 0 || value === null)
|
|
268
|
+
return void 0;
|
|
269
|
+
if (typeof value === "string" && value === "")
|
|
270
|
+
return void 0;
|
|
271
|
+
if (Array.isArray(value)) {
|
|
272
|
+
const next = value.map((v) => pruneEmptyDeep(v)).filter((v) => v !== void 0);
|
|
273
|
+
return next.length === 0 ? void 0 : next;
|
|
274
|
+
}
|
|
275
|
+
if (isPlainObject(value)) {
|
|
276
|
+
const out = {};
|
|
277
|
+
for (const [k, v] of Object.entries(value)) {
|
|
278
|
+
const pv = pruneEmptyDeep(v);
|
|
279
|
+
if (pv !== void 0)
|
|
280
|
+
out[k] = pv;
|
|
281
|
+
}
|
|
282
|
+
return Object.keys(out).length === 0 ? void 0 : out;
|
|
283
|
+
}
|
|
284
|
+
return value;
|
|
285
|
+
}
|
|
286
|
+
async function fetchList(opts) {
|
|
287
|
+
if (!adapter || typeof adapter.list !== "function")
|
|
288
|
+
return;
|
|
289
|
+
const force = Boolean(opts?.force);
|
|
290
|
+
const key = buildKey();
|
|
291
|
+
if (!force && dedupe && key) {
|
|
292
|
+
if (key === lastKey || key === activeKey)
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (abortController) {
|
|
296
|
+
try {
|
|
297
|
+
abortController.abort();
|
|
298
|
+
} catch {
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
abortController = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
302
|
+
activeKey = key;
|
|
303
|
+
const seq = requestSeq += 1;
|
|
304
|
+
activeSeq = seq;
|
|
305
|
+
loading.value = true;
|
|
306
|
+
error.value = null;
|
|
307
|
+
try {
|
|
308
|
+
const result = await adapter.list({
|
|
309
|
+
page: page.value,
|
|
310
|
+
pageSize: pageSize.value,
|
|
311
|
+
query: query.value,
|
|
312
|
+
sort: sort.value,
|
|
313
|
+
signal: abortController?.signal
|
|
314
|
+
});
|
|
315
|
+
if (seq !== activeSeq)
|
|
316
|
+
return;
|
|
317
|
+
rows.value = result.items;
|
|
318
|
+
total.value = result.total;
|
|
319
|
+
if (key)
|
|
320
|
+
lastKey = key;
|
|
321
|
+
} catch (err) {
|
|
322
|
+
if (isAbortError(err))
|
|
323
|
+
return;
|
|
324
|
+
if (seq !== activeSeq)
|
|
325
|
+
return;
|
|
326
|
+
error.value = err;
|
|
327
|
+
if (onError)
|
|
328
|
+
onError(err);
|
|
329
|
+
} finally {
|
|
330
|
+
if (seq === activeSeq) {
|
|
331
|
+
loading.value = false;
|
|
332
|
+
activeKey = null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function scheduleFetch() {
|
|
337
|
+
if (!autoFetch)
|
|
338
|
+
return;
|
|
339
|
+
if (fetchTimer) {
|
|
340
|
+
clearTimeout(fetchTimer);
|
|
341
|
+
fetchTimer = null;
|
|
342
|
+
}
|
|
343
|
+
if (debounceMs > 0) {
|
|
344
|
+
fetchTimer = setTimeout(() => {
|
|
345
|
+
fetchTimer = null;
|
|
346
|
+
void fetchList({ force: false });
|
|
347
|
+
}, debounceMs);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
void fetchList({ force: false });
|
|
351
|
+
}
|
|
352
|
+
vue.watch([query, sort, page, pageSize], () => {
|
|
353
|
+
scheduleFetch();
|
|
354
|
+
}, { deep: false });
|
|
355
|
+
function setQuery(partial, options2) {
|
|
356
|
+
const mode = options2?.mode ?? "merge";
|
|
357
|
+
const clearKeys = options2?.clearKeys ?? [];
|
|
358
|
+
const shouldPrune = Boolean(options2?.pruneEmpty);
|
|
359
|
+
const base = mode === "replace" ? {} : { ...query.value };
|
|
360
|
+
for (const k of clearKeys)
|
|
361
|
+
delete base[k];
|
|
362
|
+
const merged = mode === "replace" ? { ...partial } : { ...base, ...partial };
|
|
363
|
+
const next = shouldPrune ? pruneEmptyDeep(merged) ?? {} : merged;
|
|
364
|
+
query.value = next;
|
|
365
|
+
page.value = 1;
|
|
366
|
+
}
|
|
367
|
+
function setPage(nextPage) {
|
|
368
|
+
if (nextPage <= 0)
|
|
369
|
+
return;
|
|
370
|
+
page.value = nextPage;
|
|
371
|
+
}
|
|
372
|
+
function setPageSize(size) {
|
|
373
|
+
if (size <= 0)
|
|
374
|
+
return;
|
|
375
|
+
pageSize.value = size;
|
|
376
|
+
page.value = 1;
|
|
377
|
+
}
|
|
378
|
+
function setSort(nextSort) {
|
|
379
|
+
sort.value = nextSort;
|
|
380
|
+
page.value = 1;
|
|
381
|
+
}
|
|
382
|
+
function reset() {
|
|
383
|
+
query.value = initialQuery ?? {};
|
|
384
|
+
sort.value = null;
|
|
385
|
+
page.value = initialPage;
|
|
386
|
+
pageSize.value = initialPageSize;
|
|
387
|
+
void fetchList({ force: true });
|
|
388
|
+
}
|
|
389
|
+
async function refresh() {
|
|
390
|
+
await fetchList({ force: true });
|
|
391
|
+
}
|
|
392
|
+
scheduleFetch();
|
|
393
|
+
vue.onScopeDispose(() => {
|
|
394
|
+
if (fetchTimer) {
|
|
395
|
+
clearTimeout(fetchTimer);
|
|
396
|
+
fetchTimer = null;
|
|
397
|
+
}
|
|
398
|
+
if (abortController) {
|
|
399
|
+
try {
|
|
400
|
+
abortController.abort();
|
|
401
|
+
} catch {
|
|
402
|
+
}
|
|
403
|
+
abortController = null;
|
|
404
|
+
}
|
|
405
|
+
activeSeq = 0;
|
|
406
|
+
activeKey = null;
|
|
407
|
+
});
|
|
408
|
+
return {
|
|
409
|
+
// State
|
|
410
|
+
rows,
|
|
411
|
+
total,
|
|
412
|
+
loading,
|
|
413
|
+
error,
|
|
414
|
+
query,
|
|
415
|
+
sort,
|
|
416
|
+
page,
|
|
417
|
+
pageSize,
|
|
418
|
+
// Actions
|
|
419
|
+
refresh,
|
|
420
|
+
setQuery,
|
|
421
|
+
setPage,
|
|
422
|
+
setPageSize,
|
|
423
|
+
setSort,
|
|
424
|
+
reset
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
function useCrudRouteSync(options) {
|
|
428
|
+
const {
|
|
429
|
+
query,
|
|
430
|
+
setQuery,
|
|
431
|
+
router,
|
|
432
|
+
route,
|
|
433
|
+
queryKey = "q",
|
|
434
|
+
serialize = (q) => JSON.stringify(q),
|
|
435
|
+
deserialize = (s) => JSON.parse(s),
|
|
436
|
+
debounceMs = 300,
|
|
437
|
+
syncFromRouteMode = "replace"
|
|
438
|
+
} = options;
|
|
439
|
+
let syncTimer = null;
|
|
440
|
+
let isSyncingFromRoute = false;
|
|
441
|
+
let releaseSyncTimer = null;
|
|
442
|
+
function isPlainObject(value) {
|
|
443
|
+
return Object.prototype.toString.call(value) === "[object Object]";
|
|
444
|
+
}
|
|
445
|
+
function pruneEmptyDeep(value) {
|
|
446
|
+
if (value === void 0 || value === null)
|
|
447
|
+
return void 0;
|
|
448
|
+
if (typeof value === "string" && value === "")
|
|
449
|
+
return void 0;
|
|
450
|
+
if (Array.isArray(value)) {
|
|
451
|
+
const next = value.map((v) => pruneEmptyDeep(v)).filter((v) => v !== void 0);
|
|
452
|
+
return next.length === 0 ? void 0 : next;
|
|
453
|
+
}
|
|
454
|
+
if (isPlainObject(value)) {
|
|
455
|
+
const out = {};
|
|
456
|
+
for (const [k, v] of Object.entries(value)) {
|
|
457
|
+
const pv = pruneEmptyDeep(v);
|
|
458
|
+
if (pv !== void 0)
|
|
459
|
+
out[k] = pv;
|
|
460
|
+
}
|
|
461
|
+
return Object.keys(out).length === 0 ? void 0 : out;
|
|
462
|
+
}
|
|
463
|
+
return value;
|
|
464
|
+
}
|
|
465
|
+
function syncFromRoute() {
|
|
466
|
+
if (!route)
|
|
467
|
+
return;
|
|
468
|
+
const raw = route.query?.[queryKey];
|
|
469
|
+
if (!raw || typeof raw !== "string")
|
|
470
|
+
return;
|
|
471
|
+
try {
|
|
472
|
+
isSyncingFromRoute = true;
|
|
473
|
+
const parsed = deserialize(raw);
|
|
474
|
+
if (parsed && typeof parsed === "object") {
|
|
475
|
+
setQuery(parsed, {
|
|
476
|
+
mode: syncFromRouteMode,
|
|
477
|
+
pruneEmpty: true
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
} catch {
|
|
481
|
+
} finally {
|
|
482
|
+
if (releaseSyncTimer)
|
|
483
|
+
clearTimeout(releaseSyncTimer);
|
|
484
|
+
releaseSyncTimer = setTimeout(() => {
|
|
485
|
+
releaseSyncTimer = null;
|
|
486
|
+
isSyncingFromRoute = false;
|
|
487
|
+
}, 0);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function syncToRoute() {
|
|
491
|
+
if (!router || !route || isSyncingFromRoute)
|
|
492
|
+
return;
|
|
493
|
+
const currentQuery = query.value;
|
|
494
|
+
const normalized = pruneEmptyDeep(currentQuery);
|
|
495
|
+
const isEmpty = normalized === void 0 || isPlainObject(normalized) && Object.keys(normalized).length === 0;
|
|
496
|
+
const newRouteQuery = { ...route.query };
|
|
497
|
+
if (isEmpty) {
|
|
498
|
+
delete newRouteQuery[queryKey];
|
|
499
|
+
} else {
|
|
500
|
+
newRouteQuery[queryKey] = serialize(normalized);
|
|
501
|
+
}
|
|
502
|
+
const currentStr = route.query?.[queryKey] ?? "";
|
|
503
|
+
const newStr = newRouteQuery[queryKey] ?? "";
|
|
504
|
+
if (currentStr !== newStr) {
|
|
505
|
+
void Promise.resolve(router.replace({ query: newRouteQuery })).catch(() => {
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
vue.watch(
|
|
510
|
+
query,
|
|
511
|
+
() => {
|
|
512
|
+
if (isSyncingFromRoute)
|
|
513
|
+
return;
|
|
514
|
+
if (syncTimer) {
|
|
515
|
+
clearTimeout(syncTimer);
|
|
516
|
+
}
|
|
517
|
+
syncTimer = setTimeout(() => {
|
|
518
|
+
syncTimer = null;
|
|
519
|
+
syncToRoute();
|
|
520
|
+
}, debounceMs);
|
|
521
|
+
},
|
|
522
|
+
{ deep: true }
|
|
523
|
+
);
|
|
524
|
+
if (route) {
|
|
525
|
+
syncFromRoute();
|
|
526
|
+
}
|
|
527
|
+
vue.onScopeDispose(() => {
|
|
528
|
+
if (syncTimer) {
|
|
529
|
+
clearTimeout(syncTimer);
|
|
530
|
+
syncTimer = null;
|
|
531
|
+
}
|
|
532
|
+
if (releaseSyncTimer) {
|
|
533
|
+
clearTimeout(releaseSyncTimer);
|
|
534
|
+
releaseSyncTimer = null;
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
return {
|
|
538
|
+
syncFromRoute,
|
|
539
|
+
syncToRoute
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
function useCrudSelection(options) {
|
|
543
|
+
const { rows, getId = (row) => row?.id } = options;
|
|
544
|
+
const selectedIds = vue.shallowRef(/* @__PURE__ */ new Set());
|
|
545
|
+
const selectedRowMap = vue.shallowRef(/* @__PURE__ */ new Map());
|
|
546
|
+
const selectedRows = vue.computed(() => {
|
|
547
|
+
const ids = selectedIds.value;
|
|
548
|
+
const map = selectedRowMap.value;
|
|
549
|
+
const result = [];
|
|
550
|
+
ids.forEach((id) => {
|
|
551
|
+
const row = map.get(id);
|
|
552
|
+
if (row !== void 0)
|
|
553
|
+
result.push(row);
|
|
554
|
+
});
|
|
555
|
+
return result;
|
|
556
|
+
});
|
|
557
|
+
const selectedCount = vue.computed(() => selectedIds.value.size);
|
|
558
|
+
function syncCacheFromRows(nextRows) {
|
|
559
|
+
const ids = selectedIds.value;
|
|
560
|
+
if (ids.size === 0)
|
|
561
|
+
return;
|
|
562
|
+
const map = new Map(selectedRowMap.value);
|
|
563
|
+
for (const row of nextRows) {
|
|
564
|
+
const id = getId(row);
|
|
565
|
+
if (ids.has(id))
|
|
566
|
+
map.set(id, row);
|
|
567
|
+
}
|
|
568
|
+
selectedRowMap.value = map;
|
|
569
|
+
}
|
|
570
|
+
vue.watch(
|
|
571
|
+
rows,
|
|
572
|
+
(newRows) => {
|
|
573
|
+
syncCacheFromRows(newRows);
|
|
574
|
+
},
|
|
575
|
+
{ immediate: true }
|
|
576
|
+
);
|
|
577
|
+
vue.watch(
|
|
578
|
+
selectedIds,
|
|
579
|
+
(next) => {
|
|
580
|
+
const map = new Map(selectedRowMap.value);
|
|
581
|
+
for (const id of map.keys()) {
|
|
582
|
+
if (!next.has(id))
|
|
583
|
+
map.delete(id);
|
|
584
|
+
}
|
|
585
|
+
selectedRowMap.value = map;
|
|
586
|
+
syncCacheFromRows(rows.value);
|
|
587
|
+
},
|
|
588
|
+
{ deep: false }
|
|
589
|
+
);
|
|
590
|
+
function setSelectedIds(ids) {
|
|
591
|
+
selectedIds.value = new Set(ids);
|
|
592
|
+
}
|
|
593
|
+
function select(id) {
|
|
594
|
+
const newSet = new Set(selectedIds.value);
|
|
595
|
+
newSet.add(id);
|
|
596
|
+
selectedIds.value = newSet;
|
|
597
|
+
const row = rows.value.find((r) => getId(r) === id);
|
|
598
|
+
if (row !== void 0) {
|
|
599
|
+
const map = new Map(selectedRowMap.value);
|
|
600
|
+
map.set(id, row);
|
|
601
|
+
selectedRowMap.value = map;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
function deselect(id) {
|
|
605
|
+
const newSet = new Set(selectedIds.value);
|
|
606
|
+
newSet.delete(id);
|
|
607
|
+
selectedIds.value = newSet;
|
|
608
|
+
const map = new Map(selectedRowMap.value);
|
|
609
|
+
map.delete(id);
|
|
610
|
+
selectedRowMap.value = map;
|
|
611
|
+
}
|
|
612
|
+
function toggle(id) {
|
|
613
|
+
if (selectedIds.value.has(id)) {
|
|
614
|
+
deselect(id);
|
|
615
|
+
} else {
|
|
616
|
+
select(id);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
function selectAll() {
|
|
620
|
+
const map = new Map(selectedRowMap.value);
|
|
621
|
+
const allIds = [];
|
|
622
|
+
for (const row of rows.value) {
|
|
623
|
+
const id = getId(row);
|
|
624
|
+
allIds.push(id);
|
|
625
|
+
map.set(id, row);
|
|
626
|
+
}
|
|
627
|
+
selectedIds.value = new Set(allIds);
|
|
628
|
+
selectedRowMap.value = map;
|
|
629
|
+
}
|
|
630
|
+
function clear() {
|
|
631
|
+
selectedIds.value = /* @__PURE__ */ new Set();
|
|
632
|
+
selectedRowMap.value = /* @__PURE__ */ new Map();
|
|
633
|
+
}
|
|
634
|
+
function isSelected(id) {
|
|
635
|
+
return selectedIds.value.has(id);
|
|
636
|
+
}
|
|
637
|
+
return {
|
|
638
|
+
selectedIds,
|
|
639
|
+
selectedRows,
|
|
640
|
+
selectedCount,
|
|
641
|
+
setSelectedIds,
|
|
642
|
+
select,
|
|
643
|
+
deselect,
|
|
644
|
+
toggle,
|
|
645
|
+
selectAll,
|
|
646
|
+
clear,
|
|
647
|
+
isSelected
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// src/utils/fields.ts
|
|
652
|
+
function defineFields(fields) {
|
|
653
|
+
return fields;
|
|
654
|
+
}
|
|
655
|
+
function filterFieldsBySurface(fields, surface, ctx) {
|
|
656
|
+
return fields.filter((field) => {
|
|
657
|
+
const visibility = field.visibleIn?.[surface];
|
|
658
|
+
if (visibility === void 0 || visibility === true)
|
|
659
|
+
return true;
|
|
660
|
+
if (visibility === false)
|
|
661
|
+
return false;
|
|
662
|
+
if (typeof visibility === "function") {
|
|
663
|
+
const fullCtx = {
|
|
664
|
+
surface,
|
|
665
|
+
row: ctx?.row,
|
|
666
|
+
formModel: ctx?.formModel,
|
|
667
|
+
query: ctx?.query
|
|
668
|
+
};
|
|
669
|
+
return visibility(fullCtx);
|
|
670
|
+
}
|
|
671
|
+
return true;
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
function getFieldLabel(field) {
|
|
675
|
+
if (typeof field.label === "function") {
|
|
676
|
+
return field.label();
|
|
677
|
+
}
|
|
678
|
+
return field.label;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// src/utils/columns.ts
|
|
682
|
+
function defineColumns(columns) {
|
|
683
|
+
return columns;
|
|
684
|
+
}
|
|
685
|
+
function createColumnsFromFields(fields, options) {
|
|
686
|
+
const { overrides = {}, defaults = {}, filter } = options ?? {};
|
|
687
|
+
const visibleFields = filter ? fields.filter(filter) : fields.filter((f) => {
|
|
688
|
+
const tableVisible = f.visibleIn?.table;
|
|
689
|
+
return tableVisible !== false;
|
|
690
|
+
});
|
|
691
|
+
return visibleFields.map((field) => {
|
|
692
|
+
const override = overrides[field.key] ?? {};
|
|
693
|
+
return {
|
|
694
|
+
key: field.key,
|
|
695
|
+
label: () => getFieldLabel(field),
|
|
696
|
+
...defaults,
|
|
697
|
+
...override
|
|
698
|
+
};
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
exports.batchDeleteAction = batchDeleteAction;
|
|
703
|
+
exports.createAction = createAction;
|
|
704
|
+
exports.createColumnsFromFields = createColumnsFromFields;
|
|
705
|
+
exports.defineColumns = defineColumns;
|
|
706
|
+
exports.defineFields = defineFields;
|
|
707
|
+
exports.deleteAction = deleteAction;
|
|
708
|
+
exports.editAction = editAction;
|
|
709
|
+
exports.exportAction = exportAction;
|
|
710
|
+
exports.filterFieldsBySurface = filterFieldsBySurface;
|
|
711
|
+
exports.getFieldLabel = getFieldLabel;
|
|
712
|
+
exports.presetActions = presetActions;
|
|
713
|
+
exports.useCrudActions = useCrudActions;
|
|
714
|
+
exports.useCrudForm = useCrudForm;
|
|
715
|
+
exports.useCrudList = useCrudList;
|
|
716
|
+
exports.useCrudRouteSync = useCrudRouteSync;
|
|
717
|
+
exports.useCrudSelection = useCrudSelection;
|
|
718
|
+
//# sourceMappingURL=index.js.map
|
|
719
|
+
//# sourceMappingURL=index.js.map
|