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

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 +1 -1
  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 +174 -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,404 @@
1
+ /**
2
+ * PntTable — 统一表格组件
3
+ *
4
+ * 封装 tdesign-react Table,落地产品规范 12 章 47 条规则:
5
+ * 排序三态循环、筛选草稿/生效分离、多选三态+翻页清空、分页越界校验、
6
+ * 请求顺序控制、7 种状态管理、蓝紫双主题、同字段 sort+headerFilter 互斥。
7
+ *
8
+ * 零业务侵入:业务项目通过 fetcher 注入数据源,不依赖组件库内部 API。
9
+ */
10
+
11
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react';
12
+ import { Table } from 'tdesign-react';
13
+ import "./index.scss";
14
+ import { getPntTableTextsByLocale } from "./types";
15
+ import { useTableCore, validateColumnConflicts } from "./useTableCore";
16
+
17
+ // ============================================================
18
+ // 常量
19
+ // ============================================================
20
+ import { jsx as _jsx } from "react/jsx-runtime";
21
+ import { jsxs as _jsxs } from "react/jsx-runtime";
22
+ import { Fragment as _Fragment } from "react/jsx-runtime";
23
+ const DEFAULT_PAGE_SIZE_OPTIONS = [10, 20, 50];
24
+
25
+ // ============================================================
26
+ // 工具:列定义转换
27
+ // ============================================================
28
+
29
+ function getDefaultAlign(type) {
30
+ switch (type) {
31
+ case 'number':
32
+ case 'money':
33
+ case 'percent':
34
+ return 'right';
35
+ default:
36
+ return 'left';
37
+ }
38
+ }
39
+ function mapPntColumns(pntCols) {
40
+ return pntCols.map(col => {
41
+ const mapped = {
42
+ colKey: col.key,
43
+ title: col.title,
44
+ width: col.width,
45
+ fixed: col.fixed,
46
+ align: col.align ?? getDefaultAlign(col.type),
47
+ ellipsis: col.ellipsis === false ? undefined : col.ellipsis ?? true
48
+ };
49
+
50
+ // 排序
51
+ if (col.sort) {
52
+ mapped.sorter = true;
53
+ }
54
+
55
+ // 筛选
56
+ if (col.headerFilter) {
57
+ if (col.headerFilter.mode === 'custom') {
58
+ // 自定义组件(例:DateRangePickerPanel)
59
+ mapped.filter = {
60
+ type: 'custom',
61
+ component: col.headerFilter.component,
62
+ ...(col.headerFilter.extra ?? {})
63
+ };
64
+ } else if (col.headerFilter.options?.length) {
65
+ // 静态选项单选/多选
66
+ mapped.filter = {
67
+ type: col.headerFilter.mode === 'multiple' ? 'multiple' : 'single',
68
+ list: col.headerFilter.options,
69
+ ...(col.headerFilter.extra ?? {})
70
+ };
71
+ }
72
+ }
73
+
74
+ // 自定义渲染
75
+ if (col.cell) {
76
+ mapped.cell = ({
77
+ row,
78
+ rowIndex
79
+ }) => col.cell(row, rowIndex);
80
+ }
81
+
82
+ // 表头说明(用 title 包裹 + CSS tooltip)
83
+ if (col.help && typeof mapped.title === 'string') {
84
+ mapped.title = /*#__PURE__*/_jsxs("span", {
85
+ className: "pnt-table-col-title",
86
+ children: [mapped.title, /*#__PURE__*/_jsx("span", {
87
+ className: "pnt-table-help-icon",
88
+ title: col.help,
89
+ children: "\u24D8"
90
+ })]
91
+ });
92
+ }
93
+ return mapped;
94
+ });
95
+ }
96
+
97
+ // ============================================================
98
+ // 主组件
99
+ // ============================================================
100
+
101
+ export const PntTable = /*#__PURE__*/forwardRef((props, ref) => {
102
+ const {
103
+ tableId,
104
+ theme = 'business',
105
+ rowKey,
106
+ columns,
107
+ dataMode,
108
+ fetcher,
109
+ data,
110
+ total: clientTotal,
111
+ selectable = false,
112
+ currentPageOnly = true,
113
+ pageable = true,
114
+ initialPage = 1,
115
+ pageSize: initialPageSize = 10,
116
+ pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
117
+ empty,
118
+ locale = 'zh',
119
+ texts,
120
+ emptyText,
121
+ emptyWithFilterHint,
122
+ bordered = false,
123
+ size = 'medium',
124
+ loading: controlledLoading,
125
+ bodyMaxHeight,
126
+ businessId,
127
+ env,
128
+ timezone,
129
+ onQueryChange,
130
+ onSelectionChange,
131
+ className,
132
+ style,
133
+ rowClassName
134
+ } = props;
135
+
136
+ // --- 文案合并:texts 逐字段覆盖 > locale 内置文案 ---
137
+ const mergedTexts = useMemo(() => ({
138
+ ...getPntTableTextsByLocale(locale),
139
+ ...(texts ?? {})
140
+ }), [locale, texts]);
141
+ // 兼容既有 props:emptyText / emptyWithFilterHint 优先级高于 texts / locale
142
+ const finalEmptyText = emptyText ?? mergedTexts.empty;
143
+ const finalEmptyWithFilterHint = emptyWithFilterHint ?? mergedTexts.emptyWithFilter;
144
+
145
+ // --- selectable 配置展开 ---
146
+ const selectConfig = useMemo(() => {
147
+ if (!selectable) return null;
148
+ if (typeof selectable === 'object') return selectable;
149
+ return {}; // 空对象表示启用多选但无额外配置
150
+ }, [selectable]);
151
+ const isSelectable = !!selectConfig;
152
+ const effectiveCurrentPageOnly = selectConfig?.currentPageOnly ?? currentPageOnly;
153
+ const controlledSelectedRowKeys = selectConfig?.selectedRowKeys;
154
+ const disabledRowKeysFn = selectConfig?.disabledRowKeysFn;
155
+
156
+ // --- 受控选中行 state ---
157
+ const [internalSelectedRowKeys, setInternalSelectedRowKeys] = useState(() => controlledSelectedRowKeys ?? []);
158
+ // 同步受控 prop 变化
159
+ useEffect(() => {
160
+ if (controlledSelectedRowKeys !== undefined) {
161
+ setInternalSelectedRowKeys(controlledSelectedRowKeys);
162
+ }
163
+ }, [controlledSelectedRowKeys]);
164
+
165
+ // --- core hook ---
166
+ const core = useTableCore({
167
+ tableId,
168
+ rowKey,
169
+ columns,
170
+ dataMode,
171
+ fetcher,
172
+ data,
173
+ total: clientTotal,
174
+ initialPage,
175
+ pageSize: initialPageSize,
176
+ pageSizeOptions,
177
+ businessId,
178
+ env,
179
+ timezone,
180
+ onQueryChange,
181
+ onSelectionChange,
182
+ selectable: isSelectable,
183
+ currentPageOnly: effectiveCurrentPageOnly
184
+ });
185
+
186
+ // --- imperative handle ---
187
+ useImperativeHandle(ref, () => ({
188
+ refresh: () => core.refresh(),
189
+ reset: () => {
190
+ core.resetFilters();
191
+ core.setPage(1);
192
+ },
193
+ getQuerySnapshot: () => ({
194
+ sort: core.sort,
195
+ filters: core.appliedFilters,
196
+ page: core.page,
197
+ pageSize: core.pageSize,
198
+ total: core.total
199
+ })
200
+ }));
201
+
202
+ // --- 开发阶段互斥校验 ---
203
+ const conflicts = useMemo(() => validateColumnConflicts(columns), [columns]);
204
+ useEffect(() => {
205
+ if (conflicts.length > 0) {
206
+ conflicts.forEach(c => console.error(c));
207
+ }
208
+ }, [conflicts]);
209
+
210
+ // --- 列定义转换 ---
211
+ const tdesignCols = useMemo(() => {
212
+ const mapped = mapPntColumns(columns);
213
+ // 多选模式:插入选择列
214
+ if (isSelectable) {
215
+ mapped.unshift({
216
+ colKey: 'pnt-row-select',
217
+ type: 'multiple',
218
+ width: 64,
219
+ checkProps: disabledRowKeysFn ? ({
220
+ row
221
+ }) => ({
222
+ disabled: disabledRowKeysFn(row)
223
+ }) : undefined
224
+ });
225
+ }
226
+ return mapped;
227
+ }, [columns, isSelectable, disabledRowKeysFn]);
228
+
229
+ // --- defaultSort 映射(TDesign 用 SortInfo:{ sortBy, descending }) ---
230
+ const defaultSortInfo = useMemo(() => {
231
+ if (!core.sort) return undefined;
232
+ return {
233
+ sortBy: core.sort.field,
234
+ descending: core.sort.direction === 'desc'
235
+ };
236
+ }, [core.sort]);
237
+
238
+ // --- 筛选 filterValue 映射 ---
239
+ const filterValue = useMemo(() => {
240
+ const fv = {};
241
+ Object.entries(core.appliedFilters).forEach(([k, v]) => {
242
+ fv[k] = v;
243
+ });
244
+ return Object.keys(fv).length > 0 ? fv : undefined;
245
+ }, [core.appliedFilters]);
246
+
247
+ // --- 排序三态处理(TDesign onSortChange 返回 TableSort) ---
248
+ const handleSortChange = useCallback(sort => {
249
+ // TDesign 可能返回 SortInfo 或 SortInfo[],取第一个
250
+ const info = Array.isArray(sort) ? sort[0] : sort;
251
+ if (!info) return;
252
+ const field = info.sortBy;
253
+ const currentDir = info.descending ? 'desc' : 'asc';
254
+ core.toggleSort(field);
255
+ void currentDir; // core.toggleSort 内部会自己处理三态
256
+ }, [core]);
257
+
258
+ // --- 筛选处理 ---
259
+ const handleFilterChange = useCallback(filterVal => {
260
+ core.applyFilters(filterVal);
261
+ }, [core]);
262
+
263
+ // --- 分页处理 ---
264
+ const pagination = useMemo(() => {
265
+ if (!pageable) return undefined;
266
+ return {
267
+ total: core.total,
268
+ current: core.page,
269
+ pageSize: core.pageSize,
270
+ pageSizeOptions,
271
+ showJumper: true,
272
+ showPageSize: true,
273
+ onChange: pageInfo => {
274
+ const {
275
+ current,
276
+ pageSize: size
277
+ } = pageInfo;
278
+ if (current !== core.page) core.setPage(current);
279
+ if (size !== core.pageSize) core.setPageSize(size);
280
+ }
281
+ };
282
+ }, [pageable, core.total, core.page, core.pageSize, pageSizeOptions, core.setPage, core.setPageSize]);
283
+
284
+ // --- 加载态 ---
285
+ const isLoading = typeof controlledLoading === 'boolean' ? controlledLoading : core.status === 'loading' || core.status === 'refreshing';
286
+
287
+ // --- 空态内容(不用 Empty 组件,直接 ReactNode) ---
288
+ const emptyContent = useMemo(() => {
289
+ if (core.status === 'error') {
290
+ return /*#__PURE__*/_jsx("div", {
291
+ className: "pnt-table-empty",
292
+ children: /*#__PURE__*/_jsx("div", {
293
+ className: "pnt-table-empty-desc",
294
+ children: core.error || mergedTexts.loadFailed
295
+ })
296
+ });
297
+ }
298
+ if (core.status === 'no-permission') {
299
+ return /*#__PURE__*/_jsx("div", {
300
+ className: "pnt-table-empty",
301
+ children: /*#__PURE__*/_jsx("div", {
302
+ className: "pnt-table-empty-desc",
303
+ children: mergedTexts.noPermission
304
+ })
305
+ });
306
+ }
307
+ if (core.status === 'empty-with-filter') {
308
+ return /*#__PURE__*/_jsxs("div", {
309
+ className: "pnt-table-empty",
310
+ children: [/*#__PURE__*/_jsx("div", {
311
+ className: "pnt-table-empty-desc",
312
+ children: finalEmptyText
313
+ }), /*#__PURE__*/_jsx("div", {
314
+ className: "pnt-table-empty-hint",
315
+ children: finalEmptyWithFilterHint
316
+ })]
317
+ });
318
+ }
319
+ if (core.status === 'empty') {
320
+ if (empty) return /*#__PURE__*/_jsx(_Fragment, {
321
+ children: empty
322
+ });
323
+ return /*#__PURE__*/_jsx("div", {
324
+ className: "pnt-table-empty",
325
+ children: /*#__PURE__*/_jsx("div", {
326
+ className: "pnt-table-empty-desc",
327
+ children: finalEmptyText
328
+ })
329
+ });
330
+ }
331
+ return undefined;
332
+ }, [core.status, core.error, empty, mergedTexts, finalEmptyText, finalEmptyWithFilterHint]);
333
+
334
+ // --- 多选处理 ---
335
+ const handleSelectChange = useCallback(selectedRowKeys => {
336
+ if (!isSelectable) return;
337
+ const currentPageKeys = core.data.map(r => String(r[rowKey]));
338
+ const disabledKeys = disabledRowKeysFn ? core.data.filter(disabledRowKeysFn).map(r => String(r[rowKey])) : [];
339
+ const disabledSet = new Set(disabledKeys);
340
+
341
+ // 过滤掉禁用行的勾选
342
+ let keys = selectedRowKeys.map(String).filter(k => !disabledSet.has(k));
343
+ if (effectiveCurrentPageOnly) {
344
+ keys = keys.filter(k => currentPageKeys.includes(k));
345
+ }
346
+ if (controlledSelectedRowKeys === undefined) {
347
+ // 非受控:内部维护
348
+ setInternalSelectedRowKeys(keys);
349
+ }
350
+ const selectedRows = keys.map(k => core.data.find(r => String(r[rowKey]) === k) ?? {
351
+ [rowKey]: k
352
+ });
353
+ onSelectionChange?.(selectedRows, {
354
+ tableId,
355
+ pageKeys: currentPageKeys,
356
+ disabledKeys,
357
+ trigger: 'select'
358
+ });
359
+ }, [isSelectable, core.data, rowKey, disabledRowKeysFn, effectiveCurrentPageOnly, controlledSelectedRowKeys, tableId, onSelectionChange]);
360
+
361
+ // --- 滚动(类型宽松,TDesign 不同版本 TScroll 定义不同) ---
362
+ const scroll = useMemo(() => {
363
+ if (!bodyMaxHeight) return undefined;
364
+ return {
365
+ y: bodyMaxHeight
366
+ };
367
+ }, [bodyMaxHeight]);
368
+
369
+ // ============================================================
370
+ // 渲染
371
+ // ============================================================
372
+
373
+ return /*#__PURE__*/_jsx("div", {
374
+ "data-theme": theme,
375
+ className: `pnt-table ${className ?? ''}`,
376
+ style: style,
377
+ children: /*#__PURE__*/_jsx(Table, {
378
+ rowKey: rowKey,
379
+ data: core.data,
380
+ columns: tdesignCols,
381
+ loading: isLoading,
382
+ hover: true,
383
+ stripe: false,
384
+ bordered: bordered,
385
+ verticalAlign: "middle",
386
+ size: size,
387
+ defaultSort: defaultSortInfo,
388
+ filterValue: filterValue,
389
+ loadingProps: {
390
+ text: mergedTexts.loading
391
+ },
392
+ pagination: pagination,
393
+ scroll: scroll,
394
+ empty: emptyContent,
395
+ selectedRowKeys: isSelectable ? controlledSelectedRowKeys ?? internalSelectedRowKeys : undefined,
396
+ onSortChange: handleSortChange,
397
+ onFilterChange: handleFilterChange,
398
+ onSelectChange: handleSelectChange,
399
+ rowClassName: rowClassName
400
+ })
401
+ });
402
+ });
403
+ PntTable.displayName = 'PntTable';
404
+ export default PntTable;
@@ -0,0 +1,290 @@
1
+ /**
2
+ * PntTable 蓝紫双主题样式
3
+ *
4
+ * CSS 变量映射:一套样式两类主题
5
+ * data-theme="business" → 蓝主题(业务侧,默认)
6
+ * data-theme="internal" → 紫主题(内部平台)
7
+ *
8
+ * 规范来源:管理端组件规范 TBL-02.01 / TBL-02.02
9
+ */
10
+
11
+ // ============================================================
12
+ // CSS 变量(默认蓝主题)
13
+ // ============================================================
14
+
15
+ .pnt-table {
16
+ // 主题品牌色
17
+ --pnt-table-brand: #3f8cff;
18
+ --pnt-table-brand-hover: #2875e3;
19
+ --pnt-table-brand-active: #1f65cc;
20
+ --pnt-table-brand-soft: #eaf3ff;
21
+
22
+ // 语义色(不随主题变化)
23
+ --pnt-table-danger: #d54941;
24
+ --pnt-table-danger-soft: #fdecea;
25
+ --pnt-table-success: #2ba471;
26
+ --pnt-table-success-soft: #e3f6ee;
27
+ --pnt-table-warning: #e37318;
28
+ --pnt-table-warning-soft: #fef3e8;
29
+
30
+ // 中性色
31
+ --pnt-table-bg: #ffffff;
32
+ --pnt-table-bg-header: #f8fafd;
33
+ --pnt-table-bg-hover: #f8fafd;
34
+ --pnt-table-bg-selected: var(--pnt-table-brand-soft);
35
+ --pnt-table-border: #e5e8ef;
36
+ --pnt-table-text-primary: #1d2129;
37
+ --pnt-table-text-secondary: #4e5969;
38
+ --pnt-table-text-tertiary: #86909c;
39
+ --pnt-table-text-placeholder: #bbbfc4;
40
+
41
+ // 尺寸(TBL-02.04 / TBL-02.05)
42
+ --pnt-table-header-height: 48px;
43
+ --pnt-table-row-min-height: 48px;
44
+ --pnt-table-cell-padding-v: 12px;
45
+ --pnt-table-cell-padding-h: 16px;
46
+ }
47
+
48
+ // 紫主题覆盖
49
+ .pnt-table[data-theme='internal'] {
50
+ --pnt-table-brand: #624beb;
51
+ --pnt-table-brand-hover: #503bd1;
52
+ --pnt-table-brand-active: #412eb5;
53
+ --pnt-table-brand-soft: #f0edff;
54
+ }
55
+
56
+ // ============================================================
57
+ // 表头(TBL-03.01 / TBL-03.02 / TBL-03.03)
58
+ // ============================================================
59
+
60
+ .pnt-table .t-table__header {
61
+ background-color: var(--pnt-table-bg-header);
62
+ font-weight: 600;
63
+ color: var(--pnt-table-text-primary);
64
+ font-size: 14px;
65
+ line-height: var(--pnt-table-header-height);
66
+ }
67
+
68
+ .pnt-table .t-table__header th {
69
+ height: var(--pnt-table-header-height);
70
+ padding: 0 var(--pnt-table-cell-padding-h);
71
+ border-bottom: 1px solid var(--pnt-table-border);
72
+ border-right: none;
73
+ background-color: var(--pnt-table-bg-header);
74
+ white-space: nowrap;
75
+ overflow: hidden;
76
+ text-overflow: ellipsis;
77
+ }
78
+
79
+ // 表头单元格(左中右对齐)
80
+ .pnt-table .t-table__header th.tdesign-table-td--left { text-align: left; }
81
+ .pnt-table .t-table__header th.tdesign-table-td--center,
82
+ .pnt-table .t-table__header th.tdesign-table-td--middle { text-align: center; }
83
+ .pnt-table .t-table__header th.tdesign-table-td--right { text-align: right; }
84
+
85
+ // ============================================================
86
+ // 表体(TBL-03.03 / TBL-03.05 / TBL-03.06)
87
+ // ============================================================
88
+
89
+ .pnt-table .t-table__body td {
90
+ padding: var(--pnt-table-cell-padding-v) var(--pnt-table-cell-padding-h);
91
+ min-height: var(--pnt-table-row-min-height);
92
+ border-bottom: 1px solid var(--pnt-table-border);
93
+ font-size: 14px;
94
+ color: var(--pnt-table-text-primary);
95
+ }
96
+
97
+ // hover 态(TBL-02.03 / TBL-02.08)
98
+ .pnt-table .t-table__body tr:hover > td {
99
+ background-color: var(--pnt-table-bg-hover);
100
+ }
101
+
102
+ // 选中态(TBL-02.09 / TBL-02.10)
103
+ .pnt-table .t-table__body tr.is-selected > td {
104
+ background-color: var(--pnt-table-bg-selected);
105
+ }
106
+ // 选中后 hover 仍保持选中背景
107
+ .pnt-table .t-table__body tr.is-selected:hover > td {
108
+ background-color: var(--pnt-table-bg-selected);
109
+ }
110
+
111
+ // 斑马纹:默认关闭(TBL-02.03 明确默认无)
112
+ // 业务需要时显式传 stripe 开启,PntTable 默认 false
113
+
114
+ // ============================================================
115
+ // 对齐(TBL-03.06)
116
+ // ============================================================
117
+
118
+ .pnt-table .tdesign-table-td--left,
119
+ .pnt-table .tdesign-table-td--start { text-align: left; }
120
+ .pnt-table .tdesign-table-td--center,
121
+ .pnt-table .tdesign-table-td--middle { text-align: center; }
122
+ .pnt-table .tdesign-table-td--right,
123
+ .pnt-table .tdesign-table-td--end { text-align: right; }
124
+
125
+ // ============================================================
126
+ // 列标题 + help icon
127
+ // ============================================================
128
+
129
+ .pnt-table-col-title {
130
+ display: inline-flex;
131
+ align-items: center;
132
+ gap: 4px;
133
+
134
+ .pnt-table-help-icon {
135
+ display: inline-flex;
136
+ width: 14px;
137
+ height: 14px;
138
+ border-radius: 50%;
139
+ background: var(--pnt-table-border);
140
+ color: var(--pnt-table-text-tertiary);
141
+ font-size: 10px;
142
+ line-height: 14px;
143
+ justify-content: center;
144
+ cursor: help;
145
+ flex-shrink: 0;
146
+
147
+ &:hover {
148
+ background: var(--pnt-table-brand);
149
+ color: #fff;
150
+ }
151
+ }
152
+ }
153
+
154
+ // ============================================================
155
+ // 空态(TBL-09.03 / TBL-09.04)
156
+ // ============================================================
157
+
158
+ .pnt-table .t-table__empty,
159
+ .pnt-table .t-empty {
160
+ padding: 48px 0;
161
+ color: var(--pnt-table-text-tertiary);
162
+ font-size: 14px;
163
+ }
164
+
165
+ .pnt-table-empty-with-filter {
166
+ display: flex;
167
+ flex-direction: column;
168
+ gap: 4px;
169
+
170
+ .pnt-table-empty-hint {
171
+ font-size: 12px;
172
+ color: var(--pnt-table-text-tertiary);
173
+ }
174
+ }
175
+
176
+ // ============================================================
177
+ // 分页(TBL-07.01 / TBL-07.06)
178
+ // ============================================================
179
+
180
+ .pnt-table .t-pagination {
181
+ padding: 16px 0;
182
+ justify-content: flex-end;
183
+ gap: 12px;
184
+ font-size: 14px;
185
+ color: var(--pnt-table-text-secondary);
186
+
187
+ .t-pagination__btn {
188
+ border: none;
189
+ border-radius: 4px;
190
+ padding: 0 8px;
191
+ height: 32px;
192
+
193
+ &:hover:not(.is-disabled) {
194
+ color: var(--pnt-table-brand);
195
+ }
196
+
197
+ &.is-active {
198
+ background: var(--pnt-table-brand);
199
+ color: #fff;
200
+ }
201
+ }
202
+
203
+ .t-pagination__total {
204
+ margin-right: auto;
205
+ }
206
+
207
+ .t-pagination__size,
208
+ .t-pagination__jump {
209
+ display: flex;
210
+ align-items: center;
211
+ gap: 8px;
212
+ }
213
+ }
214
+
215
+ // ============================================================
216
+ // 加载态(TBL-09.01 / TBL-09.02)
217
+ // ============================================================
218
+
219
+ .pnt-table .t-table__loading {
220
+ position: absolute;
221
+ inset: 0;
222
+ background: rgba(255, 255, 255, 0.6);
223
+ z-index: 1;
224
+
225
+ .t-table__loading-text {
226
+ color: var(--pnt-table-text-secondary);
227
+ font-size: 14px;
228
+ }
229
+ }
230
+
231
+ // 骨架(首次加载)
232
+ .pnt-table .t-table__loading--skeleton {
233
+ .t-table__body tr td {
234
+ height: var(--pnt-table-row-min-height);
235
+ }
236
+ }
237
+
238
+ // ============================================================
239
+ // 固定列(TBL-08.01 / TBL-08.02)
240
+ // ============================================================
241
+
242
+
243
+ // 固定列跟随 hover/选中背景
244
+ .pnt-table .t-table__body tr:hover > td.t-table__cell--fixed-left,
245
+ .pnt-table .t-table__body tr:hover > td.t-table__cell--fixed-right {
246
+ background-color: var(--pnt-table-bg-hover);
247
+ }
248
+
249
+ .pnt-table .t-table__body tr.is-selected > td.t-table__cell--fixed-left,
250
+ .pnt-table .t-table__body tr.is-selected > td.t-table__cell--fixed-right {
251
+ background-color: var(--pnt-table-bg-selected);
252
+ }
253
+
254
+ // ============================================================
255
+ // 多选列(TBL-06.01 / TBL-06.02)
256
+ // ============================================================
257
+
258
+ .pnt-table .t-table__select {
259
+ width: 44px;
260
+ text-align: center;
261
+
262
+ .t-checkbox {
263
+ cursor: pointer;
264
+ }
265
+ }
266
+
267
+ // ============================================================
268
+ // 滚动容器
269
+ // ============================================================
270
+
271
+ .pnt-table .t-table__body-wrapper::-webkit-scrollbar,
272
+ .pnt-table .t-table__header-wrapper::-webkit-scrollbar {
273
+ width: 8px;
274
+ height: 8px;
275
+ }
276
+
277
+ .pnt-table .t-table__body-wrapper::-webkit-scrollbar-thumb,
278
+ .pnt-table .t-table__header-wrapper::-webkit-scrollbar-thumb {
279
+ background: #c9cdd4;
280
+ border-radius: 4px;
281
+
282
+ &:hover {
283
+ background: var(--pnt-table-brand);
284
+ }
285
+ }
286
+
287
+ .pnt-table .t-table__body-wrapper::-webkit-scrollbar-track,
288
+ .pnt-table .t-table__header-wrapper::-webkit-scrollbar-track {
289
+ background: transparent;
290
+ }