@pnt-team/pnt-component-frontend 0.0.132 → 0.0.134

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.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * PntTable — 统一表格组件
3
+ *
4
+ * 封装 tdesign-react Table,落地产品规范 12 章 47 条规则:
5
+ * 排序三态循环、筛选草稿/生效分离、多选三态+翻页清空、分页越界校验、
6
+ * 请求顺序控制、7 种状态管理、蓝紫双主题、同字段 sort+headerFilter 互斥。
7
+ *
8
+ * 零业务侵入:业务项目通过 fetcher 注入数据源,不依赖组件库内部 API。
9
+ */
10
+ /// <reference types="react" />
11
+ import './index.scss';
12
+ import type { PntTableProps, PntTableRef } from './types';
13
+ export declare const PntTable: import("react").ForwardRefExoticComponent<PntTableProps & import("react").RefAttributes<PntTableRef>>;
14
+ export default PntTable;
@@ -0,0 +1,365 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/components/PntTable/index.tsx
20
+ var PntTable_exports = {};
21
+ __export(PntTable_exports, {
22
+ PntTable: () => PntTable,
23
+ default: () => PntTable_default
24
+ });
25
+ module.exports = __toCommonJS(PntTable_exports);
26
+ var import_react = require("react");
27
+ var import_tdesign_react = require("tdesign-react");
28
+ var import_index = require("./index.scss");
29
+ var import_types = require("./types");
30
+ var import_useTableCore = require("./useTableCore");
31
+ var import_jsx_runtime = require("react/jsx-runtime");
32
+ var DEFAULT_PAGE_SIZE_OPTIONS = [10, 20, 50];
33
+ function getDefaultAlign(type) {
34
+ switch (type) {
35
+ case "number":
36
+ case "money":
37
+ case "percent":
38
+ return "right";
39
+ default:
40
+ return "left";
41
+ }
42
+ }
43
+ function mapPntColumns(pntCols) {
44
+ return pntCols.map((col) => {
45
+ var _a, _b, _c, _d, _e;
46
+ const mapped = {
47
+ colKey: col.key,
48
+ title: col.title,
49
+ width: col.width,
50
+ fixed: col.fixed,
51
+ align: (_a = col.align) != null ? _a : getDefaultAlign(col.type),
52
+ ellipsis: col.ellipsis === false ? void 0 : (_b = col.ellipsis) != null ? _b : true
53
+ };
54
+ if (col.sort) {
55
+ mapped.sorter = true;
56
+ }
57
+ if (col.headerFilter) {
58
+ if (col.headerFilter.mode === "custom") {
59
+ mapped.filter = {
60
+ type: "custom",
61
+ component: col.headerFilter.component,
62
+ ...(_c = col.headerFilter.extra) != null ? _c : {}
63
+ };
64
+ } else if ((_d = col.headerFilter.options) == null ? void 0 : _d.length) {
65
+ mapped.filter = {
66
+ type: col.headerFilter.mode === "multiple" ? "multiple" : "single",
67
+ list: col.headerFilter.options,
68
+ ...(_e = col.headerFilter.extra) != null ? _e : {}
69
+ };
70
+ }
71
+ }
72
+ if (col.cell) {
73
+ mapped.cell = ({ row, rowIndex }) => col.cell(row, rowIndex);
74
+ }
75
+ if (col.help && typeof mapped.title === "string") {
76
+ mapped.title = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "pnt-table-col-title", children: [
77
+ mapped.title,
78
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pnt-table-help-icon", title: col.help, children: "ⓘ" })
79
+ ] });
80
+ }
81
+ return mapped;
82
+ });
83
+ }
84
+ var PntTable = (0, import_react.forwardRef)((props, ref) => {
85
+ var _a;
86
+ const {
87
+ tableId,
88
+ theme = "business",
89
+ rowKey,
90
+ columns,
91
+ dataMode,
92
+ fetcher,
93
+ data,
94
+ total: clientTotal,
95
+ selectable = false,
96
+ currentPageOnly = true,
97
+ pageable = true,
98
+ initialPage = 1,
99
+ pageSize: initialPageSize = 10,
100
+ pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
101
+ empty,
102
+ locale = "zh",
103
+ texts,
104
+ emptyText,
105
+ emptyWithFilterHint,
106
+ bordered = false,
107
+ size = "medium",
108
+ loading: controlledLoading,
109
+ bodyMaxHeight,
110
+ businessId,
111
+ env,
112
+ timezone,
113
+ onQueryChange,
114
+ onSelectionChange,
115
+ className,
116
+ style,
117
+ rowClassName
118
+ } = props;
119
+ const mergedTexts = (0, import_react.useMemo)(
120
+ () => ({ ...(0, import_types.getPntTableTextsByLocale)(locale), ...texts != null ? texts : {} }),
121
+ [locale, texts]
122
+ );
123
+ const finalEmptyText = emptyText != null ? emptyText : mergedTexts.empty;
124
+ const finalEmptyWithFilterHint = emptyWithFilterHint != null ? emptyWithFilterHint : mergedTexts.emptyWithFilter;
125
+ const selectConfig = (0, import_react.useMemo)(() => {
126
+ if (!selectable)
127
+ return null;
128
+ if (typeof selectable === "object")
129
+ return selectable;
130
+ return {};
131
+ }, [selectable]);
132
+ const isSelectable = !!selectConfig;
133
+ const effectiveCurrentPageOnly = (_a = selectConfig == null ? void 0 : selectConfig.currentPageOnly) != null ? _a : currentPageOnly;
134
+ const controlledSelectedRowKeys = selectConfig == null ? void 0 : selectConfig.selectedRowKeys;
135
+ const disabledRowKeysFn = selectConfig == null ? void 0 : selectConfig.disabledRowKeysFn;
136
+ const [internalSelectedRowKeys, setInternalSelectedRowKeys] = (0, import_react.useState)(() => controlledSelectedRowKeys != null ? controlledSelectedRowKeys : []);
137
+ (0, import_react.useEffect)(() => {
138
+ if (controlledSelectedRowKeys !== void 0) {
139
+ setInternalSelectedRowKeys(controlledSelectedRowKeys);
140
+ }
141
+ }, [controlledSelectedRowKeys]);
142
+ const core = (0, import_useTableCore.useTableCore)({
143
+ tableId,
144
+ rowKey,
145
+ columns,
146
+ dataMode,
147
+ fetcher,
148
+ data,
149
+ total: clientTotal,
150
+ initialPage,
151
+ pageSize: initialPageSize,
152
+ pageSizeOptions,
153
+ businessId,
154
+ env,
155
+ timezone,
156
+ onQueryChange,
157
+ onSelectionChange,
158
+ selectable: isSelectable,
159
+ currentPageOnly: effectiveCurrentPageOnly
160
+ });
161
+ (0, import_react.useImperativeHandle)(
162
+ ref,
163
+ () => ({
164
+ refresh: () => core.refresh(),
165
+ reset: () => {
166
+ core.resetFilters();
167
+ core.setPage(1);
168
+ },
169
+ getQuerySnapshot: () => ({
170
+ sort: core.sort,
171
+ filters: core.appliedFilters,
172
+ page: core.page,
173
+ pageSize: core.pageSize,
174
+ total: core.total
175
+ })
176
+ })
177
+ );
178
+ const conflicts = (0, import_react.useMemo)(() => (0, import_useTableCore.validateColumnConflicts)(columns), [columns]);
179
+ (0, import_react.useEffect)(() => {
180
+ if (conflicts.length > 0) {
181
+ conflicts.forEach((c) => console.error(c));
182
+ }
183
+ }, [conflicts]);
184
+ const tdesignCols = (0, import_react.useMemo)(() => {
185
+ const mapped = mapPntColumns(columns);
186
+ if (isSelectable) {
187
+ mapped.unshift({
188
+ colKey: "pnt-row-select",
189
+ type: "multiple",
190
+ width: 64,
191
+ checkProps: disabledRowKeysFn ? ({ row }) => ({ disabled: disabledRowKeysFn(row) }) : void 0
192
+ });
193
+ }
194
+ return mapped;
195
+ }, [columns, isSelectable, disabledRowKeysFn]);
196
+ const defaultSortInfo = (0, import_react.useMemo)(() => {
197
+ if (!core.sort)
198
+ return void 0;
199
+ return {
200
+ sortBy: core.sort.field,
201
+ descending: core.sort.direction === "desc"
202
+ };
203
+ }, [core.sort]);
204
+ const filterValue = (0, import_react.useMemo)(() => {
205
+ const fv = {};
206
+ Object.entries(core.appliedFilters).forEach(([k, v]) => {
207
+ fv[k] = v;
208
+ });
209
+ return Object.keys(fv).length > 0 ? fv : void 0;
210
+ }, [core.appliedFilters]);
211
+ const handleSortChange = (0, import_react.useCallback)(
212
+ (sort) => {
213
+ const info = Array.isArray(sort) ? sort[0] : sort;
214
+ if (!info)
215
+ return;
216
+ const field = info.sortBy;
217
+ const currentDir = info.descending ? "desc" : "asc";
218
+ core.toggleSort(field);
219
+ },
220
+ [core]
221
+ );
222
+ const handleFilterChange = (0, import_react.useCallback)(
223
+ (filterVal) => {
224
+ core.applyFilters(filterVal);
225
+ },
226
+ [core]
227
+ );
228
+ const pagination = (0, import_react.useMemo)(() => {
229
+ if (!pageable)
230
+ return void 0;
231
+ return {
232
+ total: core.total,
233
+ current: core.page,
234
+ pageSize: core.pageSize,
235
+ pageSizeOptions,
236
+ showJumper: true,
237
+ showPageSize: true,
238
+ onChange: (pageInfo) => {
239
+ const { current, pageSize: size2 } = pageInfo;
240
+ if (current !== core.page)
241
+ core.setPage(current);
242
+ if (size2 !== core.pageSize)
243
+ core.setPageSize(size2);
244
+ }
245
+ };
246
+ }, [
247
+ pageable,
248
+ core.total,
249
+ core.page,
250
+ core.pageSize,
251
+ pageSizeOptions,
252
+ core.setPage,
253
+ core.setPageSize
254
+ ]);
255
+ const isLoading = typeof controlledLoading === "boolean" ? controlledLoading : core.status === "loading" || core.status === "refreshing";
256
+ const emptyContent = (0, import_react.useMemo)(() => {
257
+ if (core.status === "error") {
258
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-desc", children: core.error || mergedTexts.loadFailed }) });
259
+ }
260
+ if (core.status === "no-permission") {
261
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-desc", children: mergedTexts.noPermission }) });
262
+ }
263
+ if (core.status === "empty-with-filter") {
264
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-table-empty", children: [
265
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-desc", children: finalEmptyText }),
266
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-hint", children: finalEmptyWithFilterHint })
267
+ ] });
268
+ }
269
+ if (core.status === "empty") {
270
+ if (empty)
271
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: empty });
272
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-desc", children: finalEmptyText }) });
273
+ }
274
+ return void 0;
275
+ }, [
276
+ core.status,
277
+ core.error,
278
+ empty,
279
+ mergedTexts,
280
+ finalEmptyText,
281
+ finalEmptyWithFilterHint
282
+ ]);
283
+ const handleSelectChange = (0, import_react.useCallback)(
284
+ (selectedRowKeys) => {
285
+ if (!isSelectable)
286
+ return;
287
+ const currentPageKeys = core.data.map((r) => String(r[rowKey]));
288
+ const disabledKeys = disabledRowKeysFn ? core.data.filter(disabledRowKeysFn).map((r) => String(r[rowKey])) : [];
289
+ const disabledSet = new Set(disabledKeys);
290
+ let keys = selectedRowKeys.map(String).filter((k) => !disabledSet.has(k));
291
+ if (effectiveCurrentPageOnly) {
292
+ keys = keys.filter((k) => currentPageKeys.includes(k));
293
+ }
294
+ if (controlledSelectedRowKeys === void 0) {
295
+ setInternalSelectedRowKeys(keys);
296
+ }
297
+ const selectedRows = keys.map(
298
+ (k) => {
299
+ var _a2;
300
+ return (_a2 = core.data.find((r) => String(r[rowKey]) === k)) != null ? _a2 : { [rowKey]: k };
301
+ }
302
+ );
303
+ onSelectionChange == null ? void 0 : onSelectionChange(selectedRows, {
304
+ tableId,
305
+ pageKeys: currentPageKeys,
306
+ disabledKeys,
307
+ trigger: "select"
308
+ });
309
+ },
310
+ [
311
+ isSelectable,
312
+ core.data,
313
+ rowKey,
314
+ disabledRowKeysFn,
315
+ effectiveCurrentPageOnly,
316
+ controlledSelectedRowKeys,
317
+ tableId,
318
+ onSelectionChange
319
+ ]
320
+ );
321
+ const scroll = (0, import_react.useMemo)(() => {
322
+ if (!bodyMaxHeight)
323
+ return void 0;
324
+ return { y: bodyMaxHeight };
325
+ }, [bodyMaxHeight]);
326
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
327
+ "div",
328
+ {
329
+ "data-theme": theme,
330
+ className: `pnt-table ${className != null ? className : ""}`,
331
+ style,
332
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
333
+ import_tdesign_react.Table,
334
+ {
335
+ rowKey,
336
+ data: core.data,
337
+ columns: tdesignCols,
338
+ loading: isLoading,
339
+ hover: true,
340
+ stripe: false,
341
+ bordered,
342
+ verticalAlign: "middle",
343
+ size,
344
+ defaultSort: defaultSortInfo,
345
+ filterValue,
346
+ loadingProps: { text: mergedTexts.loading },
347
+ pagination,
348
+ scroll,
349
+ empty: emptyContent,
350
+ selectedRowKeys: isSelectable ? controlledSelectedRowKeys != null ? controlledSelectedRowKeys : internalSelectedRowKeys : void 0,
351
+ onSortChange: handleSortChange,
352
+ onFilterChange: handleFilterChange,
353
+ onSelectChange: handleSelectChange,
354
+ rowClassName
355
+ }
356
+ )
357
+ }
358
+ );
359
+ });
360
+ PntTable.displayName = "PntTable";
361
+ var PntTable_default = PntTable;
362
+ // Annotate the CommonJS export names for ESM import in node:
363
+ 0 && (module.exports = {
364
+ PntTable
365
+ });
@@ -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
+ }