@pnt-team/pnt-component-frontend 0.0.138 → 0.0.140

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.
Files changed (68) hide show
  1. package/lib/cjs/api/cos.d.ts +103 -0
  2. package/lib/cjs/api/cos.js +146 -0
  3. package/lib/cjs/api/index.d.ts +23 -0
  4. package/lib/cjs/api/index.js +38 -0
  5. package/lib/cjs/api/types/index.d.ts +117 -0
  6. package/lib/cjs/api/types/index.js +17 -0
  7. package/lib/cjs/assets/css/tdesign-theme.css +584 -0
  8. package/lib/cjs/assets/images/uploadImg.png +0 -0
  9. package/lib/cjs/assets/svg/inputSearch.svg +4 -0
  10. package/lib/cjs/assets/svg/inputTip.svg +5 -0
  11. package/lib/cjs/components/PntRegionSelect/index.scss +4 -3
  12. package/lib/esm/api/cos.d.ts +103 -0
  13. package/lib/esm/api/cos.js +189 -0
  14. package/lib/esm/api/index.d.ts +23 -0
  15. package/lib/esm/api/index.js +23 -0
  16. package/lib/esm/api/types/index.d.ts +117 -0
  17. package/lib/esm/api/types/index.js +1 -0
  18. package/lib/esm/assets/css/tdesign-theme.css +584 -0
  19. package/lib/esm/assets/images/uploadImg.png +0 -0
  20. package/lib/esm/assets/svg/inputSearch.svg +4 -0
  21. package/lib/esm/assets/svg/inputTip.svg +5 -0
  22. package/lib/esm/assets/svg/inputWarnTip.svg +5 -0
  23. package/lib/esm/components/PntBusinessSelect/index.d.ts +192 -0
  24. package/lib/esm/components/PntBusinessSelect/index.js +513 -0
  25. package/lib/esm/components/PntBusinessSelect/index.scss +27 -0
  26. package/lib/esm/components/PntCosUploader/index.d.ts +134 -0
  27. package/lib/esm/components/PntCosUploader/index.js +300 -0
  28. package/lib/esm/components/PntGeneralLayout/index.d.ts +114 -0
  29. package/lib/esm/components/PntGeneralLayout/index.js +103 -0
  30. package/lib/esm/components/PntGeneralLayout/index.scss +121 -0
  31. package/lib/esm/components/PntImage/index.d.ts +93 -0
  32. package/lib/esm/components/PntImage/index.js +200 -0
  33. package/lib/esm/components/PntImage/index.scss +61 -0
  34. package/lib/esm/components/PntRegionSelect/ImportDialog.d.ts +29 -0
  35. package/lib/esm/components/PntRegionSelect/ImportDialog.js +244 -0
  36. package/lib/esm/components/PntRegionSelect/index.d.ts +39 -0
  37. package/lib/esm/components/PntRegionSelect/index.js +471 -0
  38. package/lib/esm/components/PntRegionSelect/index.scss +175 -0
  39. package/lib/esm/components/PntRegionSelect/types.d.ts +181 -0
  40. package/lib/esm/components/PntRegionSelect/types.js +77 -0
  41. package/lib/esm/components/PntSecureBox/index.d.ts +122 -0
  42. package/lib/esm/components/PntSecureBox/index.js +166 -0
  43. package/lib/esm/components/PntSecureBox/index.scss +23 -0
  44. package/lib/esm/components/PntTable/index.d.ts +14 -0
  45. package/lib/esm/components/PntTable/index.js +404 -0
  46. package/lib/esm/components/PntTable/index.scss +290 -0
  47. package/lib/esm/components/PntTable/types.d.ts +250 -0
  48. package/lib/esm/components/PntTable/types.js +81 -0
  49. package/lib/esm/components/PntTable/useTableCore.d.ts +38 -0
  50. package/lib/esm/components/PntTable/useTableCore.js +499 -0
  51. package/lib/esm/components/PntUpload/index.d.ts +149 -0
  52. package/lib/esm/components/PntUpload/index.js +276 -0
  53. package/lib/esm/components/PntUpload/index.scss +101 -0
  54. package/lib/esm/config.d.ts +97 -0
  55. package/lib/esm/config.js +60 -0
  56. package/lib/esm/global.css +2 -0
  57. package/lib/esm/global.d.ts +39 -0
  58. package/lib/esm/hooks/useHighSecurity.d.ts +88 -0
  59. package/lib/esm/hooks/useHighSecurity.js +250 -0
  60. package/lib/esm/index.d.ts +16 -0
  61. package/lib/esm/index.js +33 -0
  62. package/lib/esm/utils/const.d.ts +13 -0
  63. package/lib/esm/utils/const.js +23 -0
  64. package/lib/esm/utils/index.d.ts +6 -0
  65. package/lib/esm/utils/index.js +22 -0
  66. package/lib/umd/pnt-component-frontend.min.css +1 -0
  67. package/lib/umd/pnt-component-frontend.min.js +1 -0
  68. package/package.json +1 -1
@@ -0,0 +1,499 @@
1
+ /**
2
+ * useTableCore — PntTable 核心逻辑
3
+ *
4
+ * 负责:请求顺序控制、排序三态、筛选草稿/生效分离、多选三态、分页越界校验、7 种状态管理。
5
+ * 纯 React Hook,不依赖 TDesign DOM,方便单测和版本降级。
6
+ */
7
+
8
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
9
+
10
+ // ============================================================
11
+ // 内部类型
12
+ // ============================================================
13
+
14
+ // ============================================================
15
+ // 请求顺序控制
16
+ // ============================================================
17
+
18
+ class RequestManager {
19
+ requestIdCounter = 0;
20
+ abortController = null;
21
+ nextId() {
22
+ // 取消上一请求
23
+ if (this.abortController) {
24
+ try {
25
+ this.abortController.abort();
26
+ } catch {
27
+ /* noop */
28
+ }
29
+ }
30
+ this.abortController = new AbortController();
31
+ return ++this.requestIdCounter;
32
+ }
33
+ isLatest(id) {
34
+ return id === this.requestIdCounter;
35
+ }
36
+ abortCurrent() {
37
+ if (this.abortController) {
38
+ try {
39
+ this.abortController.abort();
40
+ } catch {
41
+ /* noop */
42
+ }
43
+ this.abortController = null;
44
+ }
45
+ }
46
+ }
47
+
48
+ // ============================================================
49
+ // 排序三态循环(TBL-04.01)
50
+ // ============================================================
51
+
52
+ export function cycleSort(currentField, currentDir, targetField) {
53
+ if (currentField !== targetField) {
54
+ // 切换字段:从升序开始,清上一列
55
+ return {
56
+ field: targetField,
57
+ direction: 'asc'
58
+ };
59
+ }
60
+ // 同字段:三态循环
61
+ if (currentDir === undefined || currentDir === null) return {
62
+ field: targetField,
63
+ direction: 'asc'
64
+ };
65
+ if (currentDir === 'asc') return {
66
+ field: targetField,
67
+ direction: 'desc'
68
+ };
69
+ // 第三态:清除排序
70
+ return null;
71
+ }
72
+
73
+ // ============================================================
74
+ // 分页越界校验(TBL-07.04)
75
+ // ============================================================
76
+
77
+ export function clampPage(page, total, pageSize) {
78
+ const maxPage = Math.max(1, Math.ceil(total / pageSize));
79
+ if (page < 1) return 1;
80
+ if (page > maxPage) return maxPage;
81
+ return page;
82
+ }
83
+ export function validateJumpPage(page, total, pageSize) {
84
+ const maxPage = Math.max(1, Math.ceil(total / pageSize));
85
+ if (!Number.isInteger(page) || page < 1 || page > maxPage) {
86
+ return `请输入 1–${maxPage} 的整数页码`;
87
+ }
88
+ return null;
89
+ }
90
+
91
+ // ============================================================
92
+ // 互斥校验(TBL-01.03)
93
+ // ============================================================
94
+
95
+ export function validateColumnConflicts(columns) {
96
+ const errors = [];
97
+ columns.forEach(col => {
98
+ if (col.sort && col.headerFilter) {
99
+ errors.push(`[PntTable] 列 "${col.key}" 同时配置了 sort 和 headerFilter,违反 TBL-01.03 互斥规则。` + ` 如需该字段同时排序+筛选,表头保留 sort,筛选移至 externalFilter(独立查询区)。`);
100
+ }
101
+ });
102
+ return errors;
103
+ }
104
+
105
+ // ============================================================
106
+ // 核心 Hook
107
+ // ============================================================
108
+
109
+ export function useTableCore(props) {
110
+ const {
111
+ tableId,
112
+ rowKey,
113
+ columns,
114
+ dataMode,
115
+ fetcher,
116
+ data: clientData,
117
+ total: clientTotal,
118
+ initialPage = 1,
119
+ pageSize: initialPageSize = 10,
120
+ businessId,
121
+ env,
122
+ timezone,
123
+ onQueryChange,
124
+ onSelectionChange,
125
+ selectable = false,
126
+ currentPageOnly = true
127
+ } = props;
128
+ const reqMgrRef = useRef(new RequestManager());
129
+ const [requestId, setRequestId] = useState(0);
130
+ const [state, setState] = useState(() => {
131
+ const initialRows = dataMode === 'client' ? clientData ?? [] : [];
132
+ return {
133
+ data: initialRows,
134
+ total: dataMode === 'client' ? clientTotal ?? clientData?.length ?? 0 : 0,
135
+ status: dataMode === 'client' ? initialRows.length === 0 ? 'empty' : 'normal' : 'loading',
136
+ error: null,
137
+ sort: null,
138
+ appliedFilters: {},
139
+ page: initialPage,
140
+ pageSize: initialPageSize,
141
+ selection: new Set()
142
+ };
143
+ });
144
+
145
+ // client 模式:props.data 变化时同步行数据,并重算空态
146
+ useEffect(() => {
147
+ if (dataMode !== 'client') return;
148
+ const rows = clientData ?? [];
149
+ setState(prev => {
150
+ const hasAppliedFilter = Object.values(prev.appliedFilters).some(v => v !== undefined && v !== null && v !== '');
151
+ return {
152
+ ...prev,
153
+ data: rows,
154
+ total: clientTotal ?? rows.length,
155
+ status: rows.length === 0 ? hasAppliedFilter ? 'empty-with-filter' : 'empty' : 'normal',
156
+ error: null
157
+ };
158
+ });
159
+ }, [dataMode, clientData, clientTotal]);
160
+ const setStatePartial = useCallback(patch => {
161
+ setState(prev => ({
162
+ ...prev,
163
+ ...patch
164
+ }));
165
+ }, []);
166
+
167
+ // ============================================================
168
+ // 数据获取
169
+ // ============================================================
170
+
171
+ const triggerFetch = useCallback(async (trigger, snapshot) => {
172
+ if (dataMode !== 'server' || !fetcher) return;
173
+ const thisReqId = reqMgrRef.current.nextId();
174
+ const query = {
175
+ query: {},
176
+ // 独立查询区(组件不维护,由业务项目维护并透传)
177
+ filters: snapshot.appliedFilters,
178
+ sort: snapshot.sort,
179
+ page: snapshot.page,
180
+ pageSize: snapshot.pageSize,
181
+ businessId,
182
+ env,
183
+ timezone
184
+ };
185
+ onQueryChange?.(query, {
186
+ tableId,
187
+ trigger,
188
+ requestId: thisReqId
189
+ });
190
+ setStatePartial({
191
+ status: snapshot.page > 1 || requestId > 0 ? 'refreshing' : 'loading'
192
+ });
193
+ try {
194
+ const res = await fetcher(query);
195
+
196
+ // 旧请求晚到 → 丢弃(TBL-09.06)
197
+ if (!reqMgrRef.current.isLatest(thisReqId)) return;
198
+ if (res.noPermission) {
199
+ setStatePartial({
200
+ status: 'no-permission',
201
+ error: null,
202
+ data: [],
203
+ total: 0
204
+ });
205
+ setRequestId(thisReqId);
206
+ return;
207
+ }
208
+ if (res.code !== undefined && res.code !== 0) {
209
+ // 文案由组件层按 locale/texts 兜底,core 只透传原始错误信息
210
+ setStatePartial({
211
+ status: 'error',
212
+ error: res.msg || ''
213
+ });
214
+ setRequestId(thisReqId);
215
+ return;
216
+ }
217
+ const rows = res.rows ?? [];
218
+ setStatePartial({
219
+ data: rows,
220
+ total: res.total ?? rows.length,
221
+ status: rows.length === 0 ? Object.values(snapshot.appliedFilters).some(v => v !== undefined && v !== null && v !== '') ? 'empty-with-filter' : 'empty' : 'normal',
222
+ error: null
223
+ });
224
+ setRequestId(thisReqId);
225
+ } catch (err) {
226
+ if (!reqMgrRef.current.isLatest(thisReqId)) return;
227
+ if (err?.name === 'AbortError') return;
228
+ setStatePartial({
229
+ status: 'error',
230
+ error: err?.message || ''
231
+ });
232
+ setRequestId(thisReqId);
233
+ }
234
+ }, [dataMode, fetcher, tableId, businessId, env, timezone, onQueryChange, requestId, setStatePartial]);
235
+
236
+ // 初始加载
237
+ useEffect(() => {
238
+ if (dataMode === 'server' && fetcher) {
239
+ const page = initialPage;
240
+ const pageSize = state.pageSize;
241
+ setStatePartial({
242
+ page,
243
+ pageSize
244
+ });
245
+ triggerFetch('initial', {
246
+ sort: null,
247
+ appliedFilters: {},
248
+ page,
249
+ pageSize
250
+ });
251
+ }
252
+ // eslint-disable-next-line react-hooks/exhaustive-deps
253
+ }, [tableId, dataMode]);
254
+
255
+ // ============================================================
256
+ // 排序
257
+ // ============================================================
258
+
259
+ const toggleSort = useCallback(field => {
260
+ if (dataMode !== 'server') return;
261
+ const col = columns.find(c => c.key === field);
262
+ if (!col || col.sort === false) return;
263
+ setState(prev => {
264
+ const newSort = cycleSort(prev.sort?.field ?? null, prev.sort?.direction, field);
265
+ const clearedSelection = currentPageOnly ? new Set() : prev.selection;
266
+ const clearedPage = prev.sort?.field !== field ? prev.page : prev.page; // 换字段才回 1
267
+ const newPage = newSort ? 1 : clearedPage; // 有排序就回 1(TBL-04.05)
268
+
269
+ // 翻页/排序变更回 1 页 + 清勾选
270
+ const next = {
271
+ ...prev,
272
+ sort: newSort,
273
+ page: newPage,
274
+ selection: clearedSelection
275
+ };
276
+ if (newSort) {
277
+ triggerFetch('sort', {
278
+ sort: newSort,
279
+ appliedFilters: next.appliedFilters,
280
+ page: newPage,
281
+ pageSize: next.pageSize
282
+ });
283
+ } else if (prev.sort) {
284
+ // 第三态清除排序
285
+ triggerFetch('sort', {
286
+ sort: null,
287
+ appliedFilters: next.appliedFilters,
288
+ page: newPage,
289
+ pageSize: next.pageSize
290
+ });
291
+ }
292
+ return next;
293
+ });
294
+ }, [dataMode, columns, triggerFetch, currentPageOnly]);
295
+
296
+ // ============================================================
297
+ // 筛选(草稿/生效分离)
298
+ // ============================================================
299
+
300
+ const applyFilters = useCallback(filters => {
301
+ // 归一化:空多选归一为不限(TBL-05.03)
302
+ const normalized = {};
303
+ Object.entries(filters).forEach(([k, v]) => {
304
+ if (Array.isArray(v) && v.length === 0) return;
305
+ if (v === undefined || v === null || v === '') return;
306
+ normalized[k] = v;
307
+ });
308
+ setState(prev => {
309
+ const clearedSelection = currentPageOnly ? new Set() : prev.selection;
310
+ const next = {
311
+ ...prev,
312
+ appliedFilters: normalized,
313
+ page: 1,
314
+ // 筛选变更回 1 页(TBL-04.05)
315
+ selection: clearedSelection
316
+ };
317
+ triggerFetch('filter', {
318
+ sort: next.sort,
319
+ appliedFilters: normalized,
320
+ page: 1,
321
+ pageSize: next.pageSize
322
+ });
323
+ return next;
324
+ });
325
+ }, [triggerFetch, currentPageOnly]);
326
+ const resetFilters = useCallback(() => {
327
+ setState(prev => {
328
+ const clearedSelection = currentPageOnly ? new Set() : prev.selection;
329
+ const next = {
330
+ ...prev,
331
+ appliedFilters: {},
332
+ page: 1,
333
+ selection: clearedSelection
334
+ };
335
+ triggerFetch('reset', {
336
+ sort: next.sort,
337
+ appliedFilters: {},
338
+ page: 1,
339
+ pageSize: next.pageSize
340
+ });
341
+ return next;
342
+ });
343
+ }, [triggerFetch, currentPageOnly]);
344
+
345
+ // ============================================================
346
+ // 分页
347
+ // ============================================================
348
+
349
+ const setPage = useCallback(page => {
350
+ if (dataMode !== 'server') return;
351
+ setState(prev => {
352
+ const validated = clampPage(page, prev.total, prev.pageSize);
353
+ if (validated === prev.page) return prev;
354
+ const clearedSelection = currentPageOnly ? new Set() : prev.selection;
355
+ const next = {
356
+ ...prev,
357
+ page: validated,
358
+ selection: clearedSelection
359
+ };
360
+ triggerFetch('pagination', {
361
+ sort: next.sort,
362
+ appliedFilters: next.appliedFilters,
363
+ page: validated,
364
+ pageSize: next.pageSize
365
+ });
366
+ return next;
367
+ });
368
+ }, [dataMode, triggerFetch, currentPageOnly]);
369
+ const setPageSize = useCallback(size => {
370
+ if (dataMode !== 'server') return;
371
+ setState(prev => {
372
+ if (size === prev.pageSize) return prev;
373
+ const clearedSelection = currentPageOnly ? new Set() : prev.selection;
374
+ // pageSize 变更回 1 页(TBL-07.02)
375
+ const next = {
376
+ ...prev,
377
+ pageSize: size,
378
+ page: 1,
379
+ selection: clearedSelection
380
+ };
381
+ triggerFetch('page-size', {
382
+ sort: next.sort,
383
+ appliedFilters: next.appliedFilters,
384
+ page: 1,
385
+ pageSize: size
386
+ });
387
+ return next;
388
+ });
389
+ }, [dataMode, triggerFetch, currentPageOnly]);
390
+
391
+ // ============================================================
392
+ // 多选(三态)
393
+ // ============================================================
394
+
395
+ const toggleRow = useCallback((key, selected) => {
396
+ if (!selectable) return;
397
+ setState(prev => {
398
+ const next = new Set(prev.selection);
399
+ if (selected) next.add(key);else next.delete(key);
400
+ const selArr = Array.from(next);
401
+ onSelectionChange?.(selArr, {
402
+ tableId,
403
+ pageKeys: prev.data.map(r => String(r[rowKey])),
404
+ disabledKeys: [],
405
+ trigger: 'select'
406
+ });
407
+ return {
408
+ ...prev,
409
+ selection: next
410
+ };
411
+ });
412
+ }, [selectable, onSelectionChange, tableId, rowKey]);
413
+ const selectAllOnPage = useCallback((rowKeys, disabledKeys) => {
414
+ if (!selectable) return;
415
+ const selectableKeys = rowKeys.filter(k => !disabledKeys.includes(k));
416
+ setState(prev => {
417
+ const allSelected = selectableKeys.every(k => prev.selection.has(k));
418
+ const next = new Set(prev.selection);
419
+ if (allSelected) {
420
+ selectableKeys.forEach(k => next.delete(k));
421
+ } else {
422
+ selectableKeys.forEach(k => next.add(k));
423
+ }
424
+ const selArr = Array.from(next);
425
+ onSelectionChange?.(selArr, {
426
+ tableId,
427
+ pageKeys: rowKeys,
428
+ disabledKeys,
429
+ trigger: allSelected ? 'select-all' : 'select-all'
430
+ });
431
+ return {
432
+ ...prev,
433
+ selection: next
434
+ };
435
+ });
436
+ }, [selectable, onSelectionChange, tableId]);
437
+ const clearSelection = useCallback(() => {
438
+ setState(prev => ({
439
+ ...prev,
440
+ selection: new Set()
441
+ }));
442
+ onSelectionChange?.([], {
443
+ tableId,
444
+ pageKeys: [],
445
+ disabledKeys: [],
446
+ trigger: 'reset'
447
+ });
448
+ }, [onSelectionChange, tableId]);
449
+ const isAllSelectedOnPage = useCallback((rowKeys, disabledKeys) => {
450
+ const selectableKeys = rowKeys.filter(k => !disabledKeys.includes(k));
451
+ if (selectableKeys.length === 0) return false;
452
+ return selectableKeys.every(k => state.selection.has(k));
453
+ }, [state.selection]);
454
+ const isIndeterminateOnPage = useCallback((rowKeys, disabledKeys) => {
455
+ const selectableKeys = rowKeys.filter(k => !disabledKeys.includes(k));
456
+ if (selectableKeys.length === 0) return false;
457
+ const selectedCount = selectableKeys.filter(k => state.selection.has(k)).length;
458
+ return selectedCount > 0 && selectedCount < selectableKeys.length;
459
+ }, [state.selection]);
460
+ const refresh = useCallback(() => {
461
+ setState(prev => {
462
+ const clearedSelection = currentPageOnly ? new Set() : prev.selection;
463
+ const next = {
464
+ ...prev,
465
+ selection: clearedSelection
466
+ };
467
+ triggerFetch('reset', {
468
+ sort: next.sort,
469
+ appliedFilters: next.appliedFilters,
470
+ page: next.page,
471
+ pageSize: next.pageSize
472
+ });
473
+ return next;
474
+ });
475
+ }, [triggerFetch, currentPageOnly]);
476
+ return useMemo(() => ({
477
+ data: state.data,
478
+ total: state.total,
479
+ status: state.status,
480
+ error: state.error,
481
+ sort: state.sort,
482
+ appliedFilters: state.appliedFilters,
483
+ page: state.page,
484
+ pageSize: state.pageSize,
485
+ selection: Array.from(state.selection),
486
+ requestId,
487
+ toggleSort,
488
+ applyFilters,
489
+ resetFilters,
490
+ setPage,
491
+ setPageSize,
492
+ toggleRow,
493
+ selectAllOnPage,
494
+ clearSelection,
495
+ refresh,
496
+ isAllSelectedOnPage,
497
+ isIndeterminateOnPage
498
+ }), [state, requestId, toggleSort, applyFilters, resetFilters, setPage, setPageSize, toggleRow, selectAllOnPage, clearSelection, refresh, isAllSelectedOnPage, isIndeterminateOnPage]);
499
+ }
@@ -0,0 +1,149 @@
1
+ import type { CosScene } from "../../api/cos";
2
+ import type { FileNameStrategy, PntCosUploadOptions } from "../PntCosUploader";
3
+ import React, { FC } from 'react';
4
+ import './index.scss';
5
+ /**
6
+ * PntUpload 文案集合
7
+ * @description 所有内部展示文案均可通过 texts prop 自定义,用于多语言场景
8
+ */
9
+ export interface PntUploadTexts {
10
+ /** 上传中文案 */
11
+ uploading?: string;
12
+ /** 上传失败文案 */
13
+ uploadFailed?: string;
14
+ /** 文件大小超出限制文案 */
15
+ sizeInvalid?: string;
16
+ /** 文件类型不支持文案 */
17
+ typeInvalid?: string;
18
+ /** 选择文件文案(无文件时) */
19
+ selectFile?: string;
20
+ /** 重新选择文案(已有文件时,非图片模式) */
21
+ reselect?: string;
22
+ }
23
+ /**
24
+ * 校验/上传失败阶段
25
+ */
26
+ export type PntUploadErrorPhase = 'validate' | 'upload';
27
+ export interface PntUploadProps {
28
+ /**
29
+ * 当前已上传文件 URL(受控)
30
+ */
31
+ value?: string;
32
+ /**
33
+ * 上传成功回调
34
+ * @param fileUrl 上传后的可访问 URL
35
+ * @param cosKey COS 对象 Key
36
+ * @param file 原始文件
37
+ */
38
+ onChange?: (fileUrl: string, cosKey: string, file: File) => void;
39
+ /**
40
+ * 删除回调
41
+ * @description 由消费方清空 value
42
+ */
43
+ onRemove?: () => void;
44
+ /**
45
+ * 异常回调(校验失败 / 上传失败)
46
+ * @param err 错误对象(校验失败时 message 为对应文案)
47
+ * @param phase 失败阶段:validate=本地校验,upload=凭证获取或 COS 上传
48
+ * @param file 原始文件
49
+ */
50
+ onError?: (err: Error, phase: PntUploadErrorPhase, file: File) => void;
51
+ /**
52
+ * 业务场景(透传给 PntCosUploader)
53
+ */
54
+ scene: CosScene;
55
+ /**
56
+ * 业务类型路径 key(透传给 PntCosUploader)
57
+ */
58
+ pathKey?: string;
59
+ /**
60
+ * 业务上下文(透传给 PntCosUploader)
61
+ */
62
+ context?: Record<string, string | number>;
63
+ /**
64
+ * 网关 audience(透传给 PntCosUploader)
65
+ * @default '/player-network'
66
+ */
67
+ audience?: string;
68
+ /**
69
+ * 文件类型限制
70
+ * @description 支持 MIME('image/png')、通配('image/*')、扩展名('.zip'),可传数组
71
+ */
72
+ accept?: string | string[];
73
+ /**
74
+ * 文件大小限制(字节)
75
+ */
76
+ sizeLimit?: number;
77
+ /**
78
+ * 是否为图片预览模式
79
+ * @description true 使用 PntImage 预览;false 使用文件名 + 选择区域
80
+ * @default true
81
+ */
82
+ imagePreview?: boolean;
83
+ /**
84
+ * 图片预览区样式(imagePreview=true 生效,一般控制宽高)
85
+ */
86
+ previewStyle?: React.CSSProperties;
87
+ /**
88
+ * 默认占位图(imagePreview=true 生效)
89
+ */
90
+ defaultImg?: string;
91
+ /**
92
+ * 文件名生成策略(透传给 PntCosUploader)
93
+ * @default 'uuid'
94
+ */
95
+ fileNameStrategy?: FileNameStrategy;
96
+ /**
97
+ * 是否走 CDN 域名拼接(透传给 PntCosUploader)
98
+ * @default true
99
+ */
100
+ useCdn?: boolean;
101
+ /**
102
+ * 自定义凭证获取函数(透传给 PntCosUploader)
103
+ */
104
+ getCredential?: PntCosUploadOptions['getCredential'];
105
+ /**
106
+ * 自定义凭证响应归一化(透传给 PntCosUploader)
107
+ */
108
+ normalizeCredential?: PntCosUploadOptions['normalizeCredential'];
109
+ /**
110
+ * 多语言文案
111
+ * @description 传入即覆盖默认中文文案;仅对应字段会覆盖,未传字段仍用默认值
112
+ */
113
+ texts?: PntUploadTexts;
114
+ /**
115
+ * 禁用上传与删除
116
+ * @default false
117
+ */
118
+ disabled?: boolean;
119
+ /**
120
+ * 自定义样式
121
+ */
122
+ style?: React.CSSProperties;
123
+ /**
124
+ * 自定义类名
125
+ */
126
+ className?: string;
127
+ }
128
+ /**
129
+ * PntUpload 业务上传组件
130
+ * @description 基于 TDesign Upload + PntImage + usePntCosUploader,
131
+ * 内置 COS 凭证获取、直传、进度、校验、错误处理;支持图片预览 / 文件两种模式
132
+ *
133
+ * @example
134
+ * ```tsx
135
+ * <PntUpload
136
+ * scene="default"
137
+ * pathKey="marketing_icon"
138
+ * context={{ gameId }}
139
+ * accept="image/png"
140
+ * sizeLimit={500 * 1024}
141
+ * imagePreview
142
+ * value={iconUrl}
143
+ * onChange={(url) => setIconUrl(url)}
144
+ * onRemove={() => setIconUrl('')}
145
+ * />
146
+ * ```
147
+ */
148
+ declare const PntUpload: FC<PntUploadProps>;
149
+ export default PntUpload;