@robot-admin/request-core 0.1.2

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.js ADDED
@@ -0,0 +1,1107 @@
1
+ import axios from 'axios';
2
+ import { shallowRef, ref, reactive, computed } from 'vue';
3
+ import { useMessage, useDialog } from 'naive-ui';
4
+
5
+ // src/axios/request.ts
6
+
7
+ // src/axios/utils/helpers.ts
8
+ function sortedStringify(obj, seen = /* @__PURE__ */ new WeakSet()) {
9
+ if (obj === null || obj === void 0) {
10
+ return "";
11
+ }
12
+ if (typeof obj !== "object") {
13
+ return String(obj);
14
+ }
15
+ if (seen.has(obj)) {
16
+ throw new Error("\u68C0\u6D4B\u5230\u5FAA\u73AF\u5F15\u7528\uFF0C\u65E0\u6CD5\u751F\u6210\u7A33\u5B9A\u7684\u7F13\u5B58\u952E");
17
+ }
18
+ seen.add(obj);
19
+ if (Array.isArray(obj)) {
20
+ return JSON.stringify(obj.map((item) => sortedStringify(item, seen)));
21
+ }
22
+ const sortedKeys = Object.keys(obj).sort();
23
+ const sortedObj = {};
24
+ for (const key of sortedKeys) {
25
+ sortedObj[key] = obj[key];
26
+ }
27
+ return JSON.stringify(sortedObj);
28
+ }
29
+ function generateRequestKey(config) {
30
+ const { method = "get", url = "", params, data } = config;
31
+ const parts = [method.toUpperCase(), url];
32
+ if (params && Object.keys(params).length > 0) {
33
+ parts.push(sortedStringify(params));
34
+ }
35
+ if (data && Object.keys(data).length > 0) {
36
+ parts.push(sortedStringify(data));
37
+ }
38
+ return parts.join("|");
39
+ }
40
+ var MemoryCache = class {
41
+ constructor() {
42
+ this.cache = /* @__PURE__ */ new Map();
43
+ this.maxSize = 1e3;
44
+ // 最大缓存数量
45
+ this.accessOrder = /* @__PURE__ */ new Set();
46
+ }
47
+ // 记录访问顺序
48
+ /**
49
+ * 获取缓存
50
+ */
51
+ get(key) {
52
+ const item = this.cache.get(key);
53
+ if (!item) return null;
54
+ if (Date.now() > item.expireAt) {
55
+ this.cache.delete(key);
56
+ this.accessOrder.delete(key);
57
+ return null;
58
+ }
59
+ this.accessOrder.delete(key);
60
+ this.accessOrder.add(key);
61
+ return item.data;
62
+ }
63
+ /**
64
+ * 设置缓存
65
+ */
66
+ set(key, data, ttl) {
67
+ if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
68
+ this.evictOldest();
69
+ }
70
+ const expireAt = Date.now() + ttl;
71
+ this.cache.set(key, {
72
+ data,
73
+ expireAt
74
+ });
75
+ this.accessOrder.delete(key);
76
+ this.accessOrder.add(key);
77
+ }
78
+ /**
79
+ * 删除缓存
80
+ */
81
+ delete(key) {
82
+ this.accessOrder.delete(key);
83
+ return this.cache.delete(key);
84
+ }
85
+ /**
86
+ * 清空所有缓存
87
+ */
88
+ clear() {
89
+ this.cache.clear();
90
+ this.accessOrder.clear();
91
+ }
92
+ /**
93
+ * 清理过期缓存
94
+ */
95
+ cleanup() {
96
+ const now = Date.now();
97
+ const entries = Array.from(this.cache.entries());
98
+ for (const [key, item] of entries) {
99
+ if (now > item.expireAt) {
100
+ this.cache.delete(key);
101
+ this.accessOrder.delete(key);
102
+ }
103
+ }
104
+ }
105
+ /**
106
+ * 获取缓存大小
107
+ */
108
+ get size() {
109
+ return this.cache.size;
110
+ }
111
+ /**
112
+ * 设置最大缓存大小
113
+ */
114
+ setMaxSize(size) {
115
+ this.maxSize = size;
116
+ while (this.cache.size > this.maxSize) {
117
+ this.evictOldest();
118
+ }
119
+ }
120
+ /**
121
+ * 清理最旧的缓存(LRU)
122
+ */
123
+ evictOldest() {
124
+ const oldestKey = this.accessOrder.values().next().value;
125
+ if (oldestKey) {
126
+ this.cache.delete(oldestKey);
127
+ this.accessOrder.delete(oldestKey);
128
+ }
129
+ }
130
+ };
131
+ var globalCache = new MemoryCache();
132
+ function delay(ms) {
133
+ return new Promise((resolve) => setTimeout(resolve, ms));
134
+ }
135
+ function isNetworkError(error) {
136
+ return !error.response && Boolean(error.code) && error.code !== "ECONNABORTED" && error.code !== "ERR_CANCELED" && error.message !== "canceled" && error.message !== "Request aborted" && error.message !== "Request cancelled";
137
+ }
138
+ function isTimeoutError(error) {
139
+ return error.code === "ECONNABORTED" && error.message.includes("timeout");
140
+ }
141
+ function isRetryableStatus(status, retryableStatusCodes) {
142
+ return retryableStatusCodes.includes(status);
143
+ }
144
+ function normalizeConfig(config, defaults) {
145
+ if (config === true) {
146
+ return { ...defaults, enabled: true };
147
+ }
148
+ if (config === false) {
149
+ return { ...defaults, enabled: false };
150
+ }
151
+ if (typeof config === "object") {
152
+ return { ...defaults, ...config };
153
+ }
154
+ return defaults;
155
+ }
156
+
157
+ // src/axios/plugins/dedupe.ts
158
+ var DEFAULT_DEDUPE_CONFIG = {
159
+ enabled: true,
160
+ keyGenerator: generateRequestKey
161
+ };
162
+ var pendingRequests = /* @__PURE__ */ new Map();
163
+ function cleanupExpiredRequests() {
164
+ const now = Date.now();
165
+ const TIMEOUT = 5 * 60 * 1e3;
166
+ Array.from(pendingRequests.entries()).forEach(([key, controller]) => {
167
+ const startTime = controller._startTime || now;
168
+ if (now - startTime > TIMEOUT) {
169
+ try {
170
+ controller.abort();
171
+ } catch (error) {
172
+ console.warn("Abort error:", error);
173
+ }
174
+ pendingRequests.delete(key);
175
+ }
176
+ });
177
+ }
178
+ if (typeof window !== "undefined") {
179
+ setInterval(cleanupExpiredRequests, 3e4);
180
+ }
181
+ function onRequest(config) {
182
+ const enhancedConfig = config;
183
+ const dedupeConfig = normalizeConfig(
184
+ enhancedConfig.dedupe,
185
+ DEFAULT_DEDUPE_CONFIG
186
+ );
187
+ if (!dedupeConfig.enabled) {
188
+ return config;
189
+ }
190
+ if (config.__fromCache) {
191
+ return config;
192
+ }
193
+ const keyGenerator = dedupeConfig.keyGenerator || DEFAULT_DEDUPE_CONFIG.keyGenerator;
194
+ const requestKey = keyGenerator(config);
195
+ const existing = pendingRequests.get(requestKey);
196
+ if (existing) {
197
+ try {
198
+ existing.abort();
199
+ } catch (error) {
200
+ console.warn("Abort existing request error:", error);
201
+ }
202
+ pendingRequests.delete(requestKey);
203
+ }
204
+ if (config.signal) {
205
+ return config;
206
+ }
207
+ const controller = new AbortController();
208
+ controller._startTime = Date.now();
209
+ config.signal = controller.signal;
210
+ pendingRequests.set(requestKey, controller);
211
+ return config;
212
+ }
213
+ function onResponse(response) {
214
+ const config = response.config;
215
+ const dedupeConfig = normalizeConfig(
216
+ config.dedupe,
217
+ DEFAULT_DEDUPE_CONFIG
218
+ );
219
+ if (dedupeConfig.enabled) {
220
+ const keyGenerator = dedupeConfig.keyGenerator || DEFAULT_DEDUPE_CONFIG.keyGenerator;
221
+ const requestKey = keyGenerator(config);
222
+ pendingRequests.delete(requestKey);
223
+ }
224
+ return response;
225
+ }
226
+ function onResponseError(error) {
227
+ const config = error.config;
228
+ if (config) {
229
+ const dedupeConfig = normalizeConfig(
230
+ config.dedupe,
231
+ DEFAULT_DEDUPE_CONFIG
232
+ );
233
+ if (dedupeConfig.enabled) {
234
+ const keyGenerator = dedupeConfig.keyGenerator || DEFAULT_DEDUPE_CONFIG.keyGenerator;
235
+ const requestKey = keyGenerator(config);
236
+ pendingRequests.delete(requestKey);
237
+ }
238
+ }
239
+ return Promise.reject(error);
240
+ }
241
+ function setupDedupePlugin(instance) {
242
+ instance.interceptors.request.use(onRequest);
243
+ instance.interceptors.response.use(onResponse, onResponseError);
244
+ }
245
+ function cancelAllPendingRequests() {
246
+ pendingRequests.forEach((controller) => {
247
+ try {
248
+ controller.abort();
249
+ } catch (error) {
250
+ console.warn("Error aborting pending request:", error);
251
+ }
252
+ });
253
+ pendingRequests.clear();
254
+ }
255
+ if (typeof window !== "undefined") {
256
+ window.addEventListener("beforeunload", () => {
257
+ cancelAllPendingRequests();
258
+ });
259
+ }
260
+ function getPendingRequestCount() {
261
+ return pendingRequests.size;
262
+ }
263
+
264
+ // src/axios/plugins/cache.ts
265
+ var DEFAULT_CACHE_CONFIG = {
266
+ enabled: false,
267
+ ttl: 5 * 60 * 1e3,
268
+ // 5 分钟
269
+ forceUpdate: false
270
+ };
271
+ function shouldUseCache(config, cacheConfig) {
272
+ if (config.method?.toUpperCase() !== "GET") {
273
+ return false;
274
+ }
275
+ if (!cacheConfig.enabled) {
276
+ return false;
277
+ }
278
+ if (cacheConfig.forceUpdate) {
279
+ return false;
280
+ }
281
+ return true;
282
+ }
283
+ function createCacheResponse(cachedData, config) {
284
+ return {
285
+ data: cachedData.data,
286
+ status: cachedData.status,
287
+ statusText: `${cachedData.statusText} (from cache)`,
288
+ headers: cachedData.headers,
289
+ config
290
+ };
291
+ }
292
+ function tryGetFromCache(config, cacheConfig) {
293
+ if (!shouldUseCache(config, cacheConfig)) {
294
+ return null;
295
+ }
296
+ const requestKey = generateRequestKey(config);
297
+ const cachedData = globalCache.get(requestKey);
298
+ if (cachedData) {
299
+ return createCacheResponse(cachedData, config);
300
+ }
301
+ return null;
302
+ }
303
+ function saveToCache(response, config, cacheConfig) {
304
+ if (config.method?.toUpperCase() === "GET" && cacheConfig.enabled && response.status === 200) {
305
+ const requestKey = generateRequestKey(config);
306
+ const ttl = cacheConfig.ttl ?? DEFAULT_CACHE_CONFIG.ttl;
307
+ const cachedResponse = {
308
+ data: response.data,
309
+ status: response.status,
310
+ statusText: response.statusText,
311
+ headers: { ...response.headers }
312
+ };
313
+ globalCache.set(requestKey, cachedResponse, ttl);
314
+ }
315
+ }
316
+ function onRequest2(config) {
317
+ const enhancedConfig = config;
318
+ const cacheConfig = normalizeConfig(
319
+ enhancedConfig.cache,
320
+ DEFAULT_CACHE_CONFIG
321
+ );
322
+ const cachedResponse = tryGetFromCache(config, cacheConfig);
323
+ if (cachedResponse) {
324
+ config.__fromCache = true;
325
+ return Promise.reject({
326
+ __fromCache: true,
327
+ __cachedResponse: cachedResponse,
328
+ config
329
+ });
330
+ }
331
+ return config;
332
+ }
333
+ function onResponse2(response) {
334
+ const config = response.config;
335
+ if (config.__fromCache) {
336
+ return config.__cachedResponse;
337
+ }
338
+ const cacheConfig = normalizeConfig(
339
+ config.cache,
340
+ DEFAULT_CACHE_CONFIG
341
+ );
342
+ saveToCache(response, response.config, cacheConfig);
343
+ return response;
344
+ }
345
+ function onResponseError2(error) {
346
+ if (error.__fromCache) {
347
+ return Promise.resolve(error.__cachedResponse);
348
+ }
349
+ return Promise.reject(error);
350
+ }
351
+ function setupCachePlugin(instance) {
352
+ instance.interceptors.request.use(onRequest2);
353
+ instance.interceptors.response.use(onResponse2, onResponseError2);
354
+ }
355
+ function clearAllCache() {
356
+ globalCache.clear();
357
+ }
358
+ function clearCache(config) {
359
+ const requestKey = generateRequestKey(config);
360
+ return globalCache.delete(requestKey);
361
+ }
362
+ function cleanupExpiredCache() {
363
+ globalCache.cleanup();
364
+ }
365
+ function getCacheSize() {
366
+ return globalCache.size;
367
+ }
368
+
369
+ // src/axios/plugins/retry.ts
370
+ var DEFAULT_RETRY_CONFIG = {
371
+ enabled: false,
372
+ count: 3,
373
+ delay: 1e3,
374
+ exponentialBackoff: true,
375
+ retryableStatusCodes: [408, 429, 500, 502, 503, 504]
376
+ };
377
+ function isCancelError(error) {
378
+ return error.name === "CanceledError" || error.name === "AbortError" || error.code === "ERR_CANCELED" || error.message === "canceled" || error.message?.includes("abort");
379
+ }
380
+ function shouldRetry(error, retryConfig) {
381
+ if (!retryConfig.enabled) {
382
+ return false;
383
+ }
384
+ const config = error.config;
385
+ const currentRetryCount = config.__retryCount ?? 0;
386
+ if (currentRetryCount >= retryConfig.count) {
387
+ return false;
388
+ }
389
+ if (isCancelError(error)) {
390
+ return false;
391
+ }
392
+ if (isNetworkError(error)) {
393
+ return true;
394
+ }
395
+ if (isTimeoutError(error)) {
396
+ return true;
397
+ }
398
+ if (error.response?.status) {
399
+ return isRetryableStatus(
400
+ error.response.status,
401
+ retryConfig.retryableStatusCodes
402
+ );
403
+ }
404
+ return false;
405
+ }
406
+ function getRetryDelay(retryCount, retryConfig) {
407
+ if (!retryConfig.exponentialBackoff) {
408
+ return retryConfig.delay;
409
+ }
410
+ const calculatedDelay = retryConfig.delay * Math.pow(2, retryCount);
411
+ return Math.min(calculatedDelay, 3e4);
412
+ }
413
+ function setupRetryPlugin(instance) {
414
+ const onResponseError4 = async (error) => {
415
+ const config = error.config;
416
+ if (!config) {
417
+ return Promise.reject(error);
418
+ }
419
+ const retryConfig = normalizeConfig(
420
+ config.retry,
421
+ DEFAULT_RETRY_CONFIG
422
+ );
423
+ if (!shouldRetry(error, retryConfig)) {
424
+ return Promise.reject(error);
425
+ }
426
+ config.__retryCount = (config.__retryCount ?? 0) + 1;
427
+ const retryDelay = getRetryDelay(config.__retryCount, retryConfig);
428
+ await delay(retryDelay);
429
+ const retryConfig_ = { ...config };
430
+ const originalSignal = config.signal;
431
+ const newController = new AbortController();
432
+ if (originalSignal && typeof originalSignal.addEventListener === "function") {
433
+ originalSignal.addEventListener("abort", () => {
434
+ newController.abort();
435
+ });
436
+ }
437
+ retryConfig_.signal = newController.signal;
438
+ delete retryConfig_.__cancelId;
439
+ delete retryConfig_.__managedByCancel;
440
+ delete retryConfig_.__handling401;
441
+ return instance.request(retryConfig_);
442
+ };
443
+ instance.interceptors.response.use(void 0, onResponseError4);
444
+ }
445
+
446
+ // src/axios/plugins/cancel.ts
447
+ var DEFAULT_CANCEL_CONFIG = {
448
+ enabled: true,
449
+ whitelist: []
450
+ };
451
+ var cancelableRequests = /* @__PURE__ */ new Map();
452
+ var requestId = 0;
453
+ var CLEANUP_INTERVAL = 3e4;
454
+ var REQUEST_TIMEOUT = 3e5;
455
+ var cleanupTimer = null;
456
+ function cleanupExpiredRequests2() {
457
+ const now = Date.now();
458
+ const expiredKeys = [];
459
+ for (const [key, controller] of cancelableRequests.entries()) {
460
+ const requestStartTime = controller._startTime || now;
461
+ if (now - requestStartTime > REQUEST_TIMEOUT) {
462
+ expiredKeys.push(key);
463
+ try {
464
+ controller.abort();
465
+ } catch (error) {
466
+ console.warn("Error aborting expired request:", error);
467
+ }
468
+ }
469
+ }
470
+ expiredKeys.forEach((key) => cancelableRequests.delete(key));
471
+ }
472
+ function startCleanupTimer() {
473
+ if (cleanupTimer) {
474
+ clearInterval(cleanupTimer);
475
+ }
476
+ cleanupTimer = setInterval(cleanupExpiredRequests2, CLEANUP_INTERVAL);
477
+ }
478
+ function stopCleanupTimer() {
479
+ if (cleanupTimer) {
480
+ clearInterval(cleanupTimer);
481
+ cleanupTimer = null;
482
+ }
483
+ }
484
+ function isInWhitelist(url, whitelist) {
485
+ return whitelist.some((pattern) => pattern.test(url));
486
+ }
487
+ function onRequest3(config) {
488
+ if (typeof AbortController === "undefined") {
489
+ console.warn("AbortController is not supported in this environment");
490
+ return config;
491
+ }
492
+ const enhancedConfig = config;
493
+ const cancelConfig = normalizeConfig(
494
+ enhancedConfig.cancel,
495
+ DEFAULT_CANCEL_CONFIG
496
+ );
497
+ if (!cancelConfig.enabled) {
498
+ return config;
499
+ }
500
+ if (config.__fromCache) {
501
+ return config;
502
+ }
503
+ const url = config.url || "";
504
+ if (isInWhitelist(url, cancelConfig.whitelist)) {
505
+ return config;
506
+ }
507
+ const controller = new AbortController();
508
+ const id = `request_${++requestId}`;
509
+ controller._startTime = Date.now();
510
+ config.signal = controller.signal;
511
+ config.__cancelId = id;
512
+ cancelableRequests.set(id, controller);
513
+ return config;
514
+ }
515
+ function onResponse3(response) {
516
+ const config = response.config;
517
+ const cancelId = config.__cancelId;
518
+ if (cancelId) {
519
+ cancelableRequests.delete(cancelId);
520
+ }
521
+ return response;
522
+ }
523
+ function onResponseError3(error) {
524
+ const config = error.config;
525
+ const cancelId = config?.__cancelId;
526
+ if (cancelId) {
527
+ cancelableRequests.delete(cancelId);
528
+ }
529
+ return Promise.reject(error);
530
+ }
531
+ function setupCancelPlugin(instance) {
532
+ instance.interceptors.request.use(onRequest3);
533
+ instance.interceptors.response.use(onResponse3, onResponseError3);
534
+ }
535
+ function cancelAllRequests() {
536
+ cancelableRequests.forEach((controller) => {
537
+ try {
538
+ controller.abort();
539
+ } catch (error) {
540
+ console.warn("Error aborting request:", error);
541
+ }
542
+ });
543
+ cancelableRequests.clear();
544
+ }
545
+ if (typeof window !== "undefined") {
546
+ window.addEventListener("beforeunload", () => {
547
+ cancelAllRequests();
548
+ stopCleanupTimer();
549
+ });
550
+ startCleanupTimer();
551
+ }
552
+ function getCancelableRequestCount() {
553
+ return cancelableRequests.size;
554
+ }
555
+
556
+ // src/axios/plugins/index.ts
557
+ function setupPlugins(instance) {
558
+ setupCachePlugin(instance);
559
+ setupCancelPlugin(instance);
560
+ setupDedupePlugin(instance);
561
+ setupRetryPlugin(instance);
562
+ }
563
+
564
+ // src/axios/request.ts
565
+ var globalService = null;
566
+ function createAxiosInstance(config = {}) {
567
+ const instance = axios.create({
568
+ timeout: 5e3,
569
+ headers: {
570
+ "Content-Type": "application/json"
571
+ },
572
+ ...config
573
+ });
574
+ setupPlugins(instance);
575
+ return instance;
576
+ }
577
+ function setGlobalAxiosInstance(instance) {
578
+ globalService = instance;
579
+ }
580
+ function getGlobalAxiosInstance() {
581
+ if (!globalService) {
582
+ throw new Error(
583
+ "Axios instance not initialized. Please call createRequestCore() first."
584
+ );
585
+ }
586
+ return globalService;
587
+ }
588
+ new Proxy({}, {
589
+ get(target, prop) {
590
+ return getGlobalAxiosInstance()[prop];
591
+ }
592
+ });
593
+ var getData = async (url, config) => {
594
+ const res = await getGlobalAxiosInstance().get(url, config);
595
+ return res.data;
596
+ };
597
+ var postData = async (url, data, config) => {
598
+ const res = await getGlobalAxiosInstance().post(url, data, config);
599
+ return res.data;
600
+ };
601
+ var putData = async (url, data, config) => {
602
+ const res = await getGlobalAxiosInstance().put(url, data, config);
603
+ return res.data;
604
+ };
605
+ var deleteData = async (url, config) => {
606
+ const res = await getGlobalAxiosInstance().delete(url, config);
607
+ return res.data;
608
+ };
609
+ var onReLoginSuccess = () => {
610
+ };
611
+ var onReLoginCancel = () => {
612
+ };
613
+
614
+ // src/core.ts
615
+ var globalConfig = {
616
+ successCodes: [200, 0, "200", "0"],
617
+ fieldAliases: {
618
+ data: ["data", "list", "items", "records"],
619
+ list: ["list", "items", "records", "rows", "data"],
620
+ total: ["total", "totalCount", "count", "totalElements"]
621
+ }
622
+ };
623
+ function getGlobalConfig() {
624
+ return globalConfig;
625
+ }
626
+ function createRequestCore(config = {}) {
627
+ const {
628
+ request = {},
629
+ interceptors = {},
630
+ successCodes,
631
+ fieldAliases
632
+ } = config;
633
+ if (successCodes) {
634
+ globalConfig.successCodes = successCodes;
635
+ }
636
+ if (fieldAliases) {
637
+ globalConfig.fieldAliases = {
638
+ data: fieldAliases.data || globalConfig.fieldAliases.data,
639
+ list: fieldAliases.list || globalConfig.fieldAliases.list,
640
+ total: fieldAliases.total || globalConfig.fieldAliases.total
641
+ };
642
+ }
643
+ const axiosInstance = createAxiosInstance(request);
644
+ setGlobalAxiosInstance(axiosInstance);
645
+ if (interceptors.request) {
646
+ axiosInstance.interceptors.request.use(
647
+ interceptors.request,
648
+ interceptors.requestError
649
+ );
650
+ }
651
+ if (interceptors.response) {
652
+ axiosInstance.interceptors.response.use(
653
+ interceptors.response,
654
+ interceptors.responseError
655
+ );
656
+ }
657
+ return {
658
+ install(app) {
659
+ app.config.globalProperties.$axios = axiosInstance;
660
+ },
661
+ axiosInstance
662
+ };
663
+ }
664
+
665
+ // src/composables/useTableCrud/constants.ts
666
+ var DEFAULT_CONFIG = {
667
+ /** 默认 ID 字段名 */
668
+ idKey: "id",
669
+ /** 默认分页大小 */
670
+ pageSize: 10,
671
+ /** 默认启用分页 */
672
+ paginationEnabled: true,
673
+ /** 默认当前页 */
674
+ currentPage: 1
675
+ };
676
+ var DATA_FIELD_ALIASES = ["data", "list", "items", "records"];
677
+ var LIST_FIELD_ALIASES = [
678
+ "list",
679
+ "items",
680
+ "records",
681
+ "rows",
682
+ "data"
683
+ ];
684
+ var TOTAL_FIELD_ALIASES = [
685
+ "total",
686
+ "totalCount",
687
+ "count",
688
+ "totalElements"
689
+ ];
690
+ var SUCCESS_CODES = [200, 0, "200", "0"];
691
+ var DEFAULT_MESSAGES = {
692
+ createSuccess: "\u65B0\u589E\u6210\u529F",
693
+ updateSuccess: "\u66F4\u65B0\u6210\u529F",
694
+ deleteSuccess: "\u5220\u9664\u6210\u529F",
695
+ saveError: "\u4FDD\u5B58\u5931\u8D25",
696
+ deleteError: "\u5220\u9664\u5931\u8D25",
697
+ loadError: "\u6570\u636E\u52A0\u8F7D\u5931\u8D25",
698
+ detailError: "\u8BE6\u60C5\u83B7\u53D6\u5931\u8D25",
699
+ noDeleteApi: "\u672A\u914D\u7F6E\u5220\u9664\u63A5\u53E3"
700
+ };
701
+
702
+ // src/composables/useTableCrud/utils.ts
703
+ function getRuntimeFieldAliases() {
704
+ const config = getGlobalConfig();
705
+ return {
706
+ data: config.fieldAliases.data || DATA_FIELD_ALIASES,
707
+ list: config.fieldAliases.list || LIST_FIELD_ALIASES,
708
+ total: config.fieldAliases.total || TOTAL_FIELD_ALIASES
709
+ };
710
+ }
711
+ function getRuntimeSuccessCodes() {
712
+ const config = getGlobalConfig();
713
+ return config.successCodes || SUCCESS_CODES;
714
+ }
715
+ var FieldFinder = {
716
+ /**
717
+ * 查找第一个存在的字段值
718
+ */
719
+ findFirst(obj, aliases, defaultValue) {
720
+ if (!obj || typeof obj !== "object") return defaultValue;
721
+ for (const key of aliases) {
722
+ if (key in obj && obj[key] !== void 0) {
723
+ return obj[key];
724
+ }
725
+ }
726
+ return defaultValue;
727
+ },
728
+ /**
729
+ * 查找第一个存在的数字字段
730
+ */
731
+ findNumber(obj, aliases, defaultValue = 0) {
732
+ const value = this.findFirst(obj, aliases, defaultValue);
733
+ return Number(value) || defaultValue;
734
+ }
735
+ };
736
+ var ResponseNormalizer = {
737
+ /**
738
+ * 判断响应是否成功
739
+ */
740
+ isSuccess(res) {
741
+ if (typeof res.success === "boolean") return res.success;
742
+ const successCodes = getRuntimeSuccessCodes();
743
+ return successCodes.includes(res.code) || successCodes.includes(String(res.code));
744
+ },
745
+ /**
746
+ * 标准化响应数据(提取 data 层)
747
+ */
748
+ normalize(res) {
749
+ if (!res || typeof res !== "object" || !("data" in res)) {
750
+ return { data: res, success: true, raw: res };
751
+ }
752
+ const aliases = getRuntimeFieldAliases();
753
+ return {
754
+ data: FieldFinder.findFirst(res, aliases.data, res),
755
+ success: ResponseNormalizer.isSuccess(res),
756
+ raw: res
757
+ };
758
+ }
759
+ };
760
+ var UrlUtils = {
761
+ /**
762
+ * 构建 URL(处理路径参数)
763
+ */
764
+ buildUrl(endpoint, id) {
765
+ if (id !== void 0 && endpoint.includes(":id")) {
766
+ return endpoint.replace(":id", String(id));
767
+ }
768
+ return endpoint;
769
+ }
770
+ };
771
+ var DataExtractor = {
772
+ /**
773
+ * 从响应中提取列表数据(支持多种格式)
774
+ *
775
+ * 支持的响应结构:
776
+ * 1. { code: 0, data: { list: [...], total: 10 } } // 嵌套结构(最常见)
777
+ * 2. { data: { items: [...], total: 10 } } // 嵌套结构
778
+ * 3. { list: [...], total: 10 } // 扁平结构
779
+ * 4. { items: [...], totalCount: 10 } // 不同字段名
780
+ * 5. { data: [...] } // 直接数组
781
+ * 6. [...] // 纯数组
782
+ */
783
+ extractList(response) {
784
+ const normalized = ResponseNormalizer.normalize(response);
785
+ const dataLayer = normalized.data ?? response;
786
+ const aliases = getRuntimeFieldAliases();
787
+ const list = FieldFinder.findFirst(dataLayer, aliases.list, []);
788
+ const total = FieldFinder.findNumber(dataLayer, aliases.total, 0);
789
+ return {
790
+ items: Array.isArray(list) ? list : [],
791
+ total
792
+ };
793
+ },
794
+ /**
795
+ * 从响应中提取详情数据
796
+ */
797
+ extractDetail(response) {
798
+ const normalized = ResponseNormalizer.normalize(response);
799
+ return normalized.data;
800
+ }
801
+ };
802
+ var RowUtils = {
803
+ /**
804
+ * 从数组中查找行索引
805
+ */
806
+ findIndex(items, idKey, id) {
807
+ return items.findIndex((item) => item[idKey] === id);
808
+ },
809
+ /**
810
+ * 从数组中移除行
811
+ */
812
+ remove(items, idKey, id) {
813
+ const index = this.findIndex(items, idKey, id);
814
+ if (index !== -1) {
815
+ items.splice(index, 1);
816
+ return true;
817
+ }
818
+ return false;
819
+ },
820
+ /**
821
+ * 生成默认 ID
822
+ */
823
+ generateId() {
824
+ return Date.now() + Math.floor(Math.random() * 1e3);
825
+ }
826
+ };
827
+
828
+ // src/composables/useTableCrud/useTableCrud.ts
829
+ function useTableCrud(config) {
830
+ const {
831
+ api,
832
+ columns,
833
+ customActions = [],
834
+ detail: detailConfig,
835
+ idKey = DEFAULT_CONFIG.idKey,
836
+ defaultPageSize = DEFAULT_CONFIG.pageSize,
837
+ defaultPaginationEnabled = DEFAULT_CONFIG.paginationEnabled,
838
+ autoLoad = true,
839
+ extractListData
840
+ } = config;
841
+ const message = useMessage();
842
+ const dialog = useDialog();
843
+ const data = shallowRef([]);
844
+ const total = ref(0);
845
+ const loading = ref(false);
846
+ const tableRef = ref();
847
+ const paginationEnabled = ref(defaultPaginationEnabled);
848
+ const page = reactive({
849
+ current: DEFAULT_CONFIG.currentPage,
850
+ size: defaultPageSize
851
+ });
852
+ const detailVisible = ref(false);
853
+ const detailData = ref(null);
854
+ const detailTitle = ref("");
855
+ const detail = {
856
+ visible: detailVisible,
857
+ data: detailData,
858
+ title: detailTitle,
859
+ show: (row) => {
860
+ detailData.value = row;
861
+ detailTitle.value = `\u8BE6\u60C5 - ${row.name || row[idKey]}`;
862
+ detailVisible.value = true;
863
+ },
864
+ close: () => {
865
+ detailVisible.value = false;
866
+ detailData.value = null;
867
+ detailTitle.value = "";
868
+ }
869
+ };
870
+ const refresh = async () => {
871
+ if (!api.list) return;
872
+ loading.value = true;
873
+ try {
874
+ const queryParams = paginationEnabled.value ? { page: page.current, pageSize: page.size } : {};
875
+ const response = await getData(api.list, { params: queryParams });
876
+ const extracted = extractListData ? extractListData(response) : DataExtractor.extractList(response);
877
+ data.value = extracted.items;
878
+ total.value = extracted.total;
879
+ } catch (error) {
880
+ console.error("[useTableCrud] \u6570\u636E\u52A0\u8F7D\u5931\u8D25:", error);
881
+ message.error(DEFAULT_MESSAGES.loadError);
882
+ } finally {
883
+ loading.value = false;
884
+ }
885
+ };
886
+ const getDetail = async (row) => {
887
+ if (!api.get) {
888
+ detail.show(row);
889
+ return row;
890
+ }
891
+ loading.value = true;
892
+ try {
893
+ const url = UrlUtils.buildUrl(api.get, row[idKey]);
894
+ const response = await getData(url, {});
895
+ const detailData2 = DataExtractor.extractDetail(response);
896
+ if (detailData2) {
897
+ detail.show(detailData2);
898
+ return detailData2;
899
+ }
900
+ return null;
901
+ } catch (error) {
902
+ console.error("[useTableCrud] \u8BE6\u60C5\u83B7\u53D6\u5931\u8D25:", error);
903
+ message.error(DEFAULT_MESSAGES.detailError);
904
+ return null;
905
+ } finally {
906
+ loading.value = false;
907
+ }
908
+ };
909
+ const create = async (row) => {
910
+ if (!api.create) {
911
+ message.warning("\u672A\u914D\u7F6E\u65B0\u589E\u63A5\u53E3");
912
+ return;
913
+ }
914
+ loading.value = true;
915
+ try {
916
+ await postData(api.create, row);
917
+ message.success(DEFAULT_MESSAGES.createSuccess);
918
+ await refresh();
919
+ } catch (error) {
920
+ console.error("[useTableCrud] \u65B0\u589E\u5931\u8D25:", error);
921
+ message.error("\u65B0\u589E\u5931\u8D25");
922
+ throw error;
923
+ } finally {
924
+ loading.value = false;
925
+ }
926
+ };
927
+ const save = async (row) => {
928
+ if (!api.update) {
929
+ message.warning("\u672A\u914D\u7F6E\u66F4\u65B0\u63A5\u53E3");
930
+ return;
931
+ }
932
+ loading.value = true;
933
+ try {
934
+ const url = UrlUtils.buildUrl(api.update, row[idKey]);
935
+ await putData(url, row);
936
+ message.success(DEFAULT_MESSAGES.updateSuccess);
937
+ await refresh();
938
+ } catch (error) {
939
+ console.error("[useTableCrud] \u66F4\u65B0\u5931\u8D25:", error);
940
+ message.error(DEFAULT_MESSAGES.saveError);
941
+ throw error;
942
+ } finally {
943
+ loading.value = false;
944
+ }
945
+ };
946
+ const remove = async (row) => {
947
+ if (!api.remove) {
948
+ message.warning(DEFAULT_MESSAGES.noDeleteApi);
949
+ return;
950
+ }
951
+ loading.value = true;
952
+ try {
953
+ const url = UrlUtils.buildUrl(api.remove, row[idKey]);
954
+ await deleteData(url);
955
+ message.success(DEFAULT_MESSAGES.deleteSuccess);
956
+ RowUtils.remove(data.value, idKey, row[idKey]);
957
+ await refresh();
958
+ } catch (error) {
959
+ console.error("[useTableCrud] \u5220\u9664\u5931\u8D25:", error);
960
+ message.error(DEFAULT_MESSAGES.deleteError);
961
+ throw error;
962
+ } finally {
963
+ loading.value = false;
964
+ }
965
+ };
966
+ const batchRemove = async (rows) => {
967
+ if (!rows || rows.length === 0) {
968
+ message.warning("\u8BF7\u9009\u62E9\u8981\u5220\u9664\u7684\u6570\u636E");
969
+ return;
970
+ }
971
+ if (!api.remove && !api.batchRemove) {
972
+ message.warning(DEFAULT_MESSAGES.noDeleteApi);
973
+ return;
974
+ }
975
+ loading.value = true;
976
+ try {
977
+ if (api.batchRemove) {
978
+ const ids = rows.map((row) => row[idKey]);
979
+ await postData(api.batchRemove, { ids });
980
+ } else {
981
+ await Promise.all(
982
+ rows.map((row) => {
983
+ const url = UrlUtils.buildUrl(api.remove, row[idKey]);
984
+ return deleteData(url);
985
+ })
986
+ );
987
+ }
988
+ message.success(`\u6210\u529F\u5220\u9664 ${rows.length} \u6761\u6570\u636E`);
989
+ await refresh();
990
+ } catch (error) {
991
+ console.error("[useTableCrud] \u6279\u91CF\u5220\u9664\u5931\u8D25:", error);
992
+ message.error("\u6279\u91CF\u5220\u9664\u5931\u8D25");
993
+ throw error;
994
+ } finally {
995
+ loading.value = false;
996
+ }
997
+ };
998
+ const handleCancel = async () => {
999
+ await refresh();
1000
+ };
1001
+ const handlePaginationChange = (pageNum, pageSize) => {
1002
+ page.current = pageNum;
1003
+ page.size = pageSize;
1004
+ refresh();
1005
+ };
1006
+ const handleRowDelete = (deletedRow) => {
1007
+ RowUtils.remove(data.value, idKey, deletedRow[idKey]);
1008
+ };
1009
+ const pagination = computed(() => {
1010
+ if (!paginationEnabled.value) return false;
1011
+ return {
1012
+ enabled: true,
1013
+ page: page.current,
1014
+ pageSize: page.size
1015
+ };
1016
+ });
1017
+ const createActionContext = (index) => ({
1018
+ data: data.value,
1019
+ index,
1020
+ page,
1021
+ paginationEnabled: paginationEnabled.value,
1022
+ message,
1023
+ dialog,
1024
+ refresh
1025
+ });
1026
+ const actions = computed(() => {
1027
+ const result = {};
1028
+ if (api.update) {
1029
+ result.edit = async (row) => {
1030
+ try {
1031
+ await save(row);
1032
+ return { data: row, error: null };
1033
+ } catch (error) {
1034
+ return { data: null, error };
1035
+ }
1036
+ };
1037
+ }
1038
+ if (api.remove) {
1039
+ result.delete = async (row) => {
1040
+ try {
1041
+ await remove(row);
1042
+ return { data: { success: true }, error: null };
1043
+ } catch (error) {
1044
+ return { data: null, error };
1045
+ }
1046
+ };
1047
+ }
1048
+ if (api.get) {
1049
+ result.detail = async (row) => {
1050
+ try {
1051
+ const detailData2 = await getDetail(row);
1052
+ return { data: detailData2, error: null };
1053
+ } catch (error) {
1054
+ return { data: null, error };
1055
+ }
1056
+ };
1057
+ }
1058
+ if (customActions.length > 0) {
1059
+ result.custom = customActions.map((action) => ({
1060
+ key: action.key,
1061
+ label: action.label,
1062
+ icon: action.icon,
1063
+ type: action.type || "default",
1064
+ onClick: (row, index) => {
1065
+ const context = createActionContext(index);
1066
+ return action.handler(row, context);
1067
+ }
1068
+ }));
1069
+ }
1070
+ return result;
1071
+ });
1072
+ if (autoLoad) {
1073
+ refresh();
1074
+ }
1075
+ return {
1076
+ // 数据状态
1077
+ data,
1078
+ loading,
1079
+ total,
1080
+ // 表格配置
1081
+ columns: computed(() => columns),
1082
+ actions,
1083
+ tableRef,
1084
+ // 分页
1085
+ page,
1086
+ paginationEnabled,
1087
+ pagination,
1088
+ // 核心方法
1089
+ refresh,
1090
+ create,
1091
+ save,
1092
+ remove,
1093
+ batchRemove,
1094
+ getDetail,
1095
+ // 事件处理
1096
+ handleCancel,
1097
+ handlePaginationChange,
1098
+ handleRowDelete,
1099
+ // 详情弹窗
1100
+ detail,
1101
+ detailConfig
1102
+ };
1103
+ }
1104
+
1105
+ export { cancelAllPendingRequests, cancelAllRequests, cleanupExpiredCache, clearAllCache, clearCache, createAxiosInstance, createRequestCore, deleteData, getCacheSize, getCancelableRequestCount, getData, getGlobalConfig, getPendingRequestCount, onReLoginCancel, onReLoginSuccess, postData, putData, useTableCrud };
1106
+ //# sourceMappingURL=index.js.map
1107
+ //# sourceMappingURL=index.js.map