@pnt-team/pnt-component-frontend 0.0.130 → 0.0.133

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.
@@ -63,8 +63,12 @@ var PntSecureBox = (prop) => {
63
63
  const unlocked = isSecurity ? isUnlocked(storeKey) : maskPlain ? plainViewed : true;
64
64
  const displayValue = unlocked ? isSecurity ? securityData[storeKey] || "******" : dataValue : "******";
65
65
  (0, import_react.useEffect)(() => {
66
- onSecurityDataChange == null ? void 0 : onSecurityDataChange(securityData);
67
- }, [securityData, onSecurityDataChange]);
66
+ if (!onSecurityDataChange)
67
+ return;
68
+ const ownValue = securityData[storeKey];
69
+ const payload = ownValue !== void 0 && securityData[dataKey] !== ownValue ? { ...securityData, [dataKey]: ownValue } : securityData;
70
+ onSecurityDataChange(payload);
71
+ }, [securityData, onSecurityDataChange, storeKey, dataKey]);
68
72
  const handleView = () => {
69
73
  if (isSecurity) {
70
74
  requestSecurityData({
@@ -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,359 @@
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_useTableCore = require("./useTableCore");
30
+ var import_jsx_runtime = require("react/jsx-runtime");
31
+ var DEFAULT_PAGE_SIZE_OPTIONS = [10, 20, 50];
32
+ function getDefaultAlign(type) {
33
+ switch (type) {
34
+ case "number":
35
+ case "money":
36
+ case "percent":
37
+ return "right";
38
+ default:
39
+ return "left";
40
+ }
41
+ }
42
+ function mapPntColumns(pntCols) {
43
+ return pntCols.map((col) => {
44
+ var _a, _b, _c, _d, _e;
45
+ const mapped = {
46
+ colKey: col.key,
47
+ title: col.title,
48
+ width: col.width,
49
+ fixed: col.fixed,
50
+ align: (_a = col.align) != null ? _a : getDefaultAlign(col.type),
51
+ ellipsis: col.ellipsis === false ? void 0 : (_b = col.ellipsis) != null ? _b : true
52
+ };
53
+ if (col.sort) {
54
+ mapped.sorter = true;
55
+ }
56
+ if (col.headerFilter) {
57
+ if (col.headerFilter.mode === "custom") {
58
+ mapped.filter = {
59
+ type: "custom",
60
+ component: col.headerFilter.component,
61
+ ...(_c = col.headerFilter.extra) != null ? _c : {}
62
+ };
63
+ } else if ((_d = col.headerFilter.options) == null ? void 0 : _d.length) {
64
+ mapped.filter = {
65
+ type: col.headerFilter.mode === "multiple" ? "multiple" : "single",
66
+ list: col.headerFilter.options,
67
+ ...(_e = col.headerFilter.extra) != null ? _e : {}
68
+ };
69
+ }
70
+ }
71
+ if (col.cell) {
72
+ mapped.cell = ({ row, rowIndex }) => col.cell(row, rowIndex);
73
+ }
74
+ if (col.help && typeof mapped.title === "string") {
75
+ mapped.title = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "pnt-table-col-title", children: [
76
+ mapped.title,
77
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pnt-table-help-icon", title: col.help, children: "ⓘ" })
78
+ ] });
79
+ }
80
+ return mapped;
81
+ });
82
+ }
83
+ var PntTable = (0, import_react.forwardRef)((props, ref) => {
84
+ var _a;
85
+ const {
86
+ tableId,
87
+ theme = "business",
88
+ rowKey,
89
+ columns,
90
+ dataMode,
91
+ fetcher,
92
+ data,
93
+ total: clientTotal,
94
+ selectable = false,
95
+ currentPageOnly = true,
96
+ pageable = true,
97
+ initialPage = 1,
98
+ pageSize: initialPageSize = 10,
99
+ pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
100
+ empty,
101
+ emptyText = "暂无数据",
102
+ emptyWithFilterHint = "调整条件或重置查询",
103
+ bordered = false,
104
+ size = "medium",
105
+ loading: controlledLoading,
106
+ bodyMaxHeight,
107
+ businessId,
108
+ env,
109
+ timezone,
110
+ onQueryChange,
111
+ onSelectionChange,
112
+ className,
113
+ style,
114
+ rowClassName
115
+ } = props;
116
+ const selectConfig = (0, import_react.useMemo)(() => {
117
+ if (!selectable)
118
+ return null;
119
+ if (typeof selectable === "object")
120
+ return selectable;
121
+ return {};
122
+ }, [selectable]);
123
+ const isSelectable = !!selectConfig;
124
+ const effectiveCurrentPageOnly = (_a = selectConfig == null ? void 0 : selectConfig.currentPageOnly) != null ? _a : currentPageOnly;
125
+ const controlledSelectedRowKeys = selectConfig == null ? void 0 : selectConfig.selectedRowKeys;
126
+ const disabledRowKeysFn = selectConfig == null ? void 0 : selectConfig.disabledRowKeysFn;
127
+ const [internalSelectedRowKeys, setInternalSelectedRowKeys] = (0, import_react.useState)(() => controlledSelectedRowKeys != null ? controlledSelectedRowKeys : []);
128
+ (0, import_react.useEffect)(() => {
129
+ if (controlledSelectedRowKeys !== void 0) {
130
+ setInternalSelectedRowKeys(controlledSelectedRowKeys);
131
+ }
132
+ }, [controlledSelectedRowKeys]);
133
+ const core = (0, import_useTableCore.useTableCore)({
134
+ tableId,
135
+ rowKey,
136
+ columns,
137
+ dataMode,
138
+ fetcher,
139
+ data,
140
+ total: clientTotal,
141
+ initialPage,
142
+ pageSize: initialPageSize,
143
+ pageSizeOptions,
144
+ businessId,
145
+ env,
146
+ timezone,
147
+ onQueryChange,
148
+ onSelectionChange,
149
+ selectable: isSelectable,
150
+ currentPageOnly: effectiveCurrentPageOnly
151
+ });
152
+ (0, import_react.useImperativeHandle)(
153
+ ref,
154
+ () => ({
155
+ refresh: () => core.refresh(),
156
+ reset: () => {
157
+ core.resetFilters();
158
+ core.setPage(1);
159
+ },
160
+ getQuerySnapshot: () => ({
161
+ sort: core.sort,
162
+ filters: core.appliedFilters,
163
+ page: core.page,
164
+ pageSize: core.pageSize,
165
+ total: core.total
166
+ })
167
+ })
168
+ );
169
+ const conflicts = (0, import_react.useMemo)(() => (0, import_useTableCore.validateColumnConflicts)(columns), [columns]);
170
+ (0, import_react.useEffect)(() => {
171
+ if (conflicts.length > 0) {
172
+ conflicts.forEach((c) => console.error(c));
173
+ }
174
+ }, [conflicts]);
175
+ const tdesignCols = (0, import_react.useMemo)(() => {
176
+ const mapped = mapPntColumns(columns);
177
+ if (isSelectable) {
178
+ mapped.unshift({
179
+ colKey: "pnt-row-select",
180
+ type: "multiple",
181
+ width: 64,
182
+ checkProps: disabledRowKeysFn ? ({ row }) => ({ disabled: disabledRowKeysFn(row) }) : void 0
183
+ });
184
+ }
185
+ return mapped;
186
+ }, [columns, isSelectable, disabledRowKeysFn]);
187
+ const defaultSortInfo = (0, import_react.useMemo)(() => {
188
+ if (!core.sort)
189
+ return void 0;
190
+ return {
191
+ sortBy: core.sort.field,
192
+ descending: core.sort.direction === "desc"
193
+ };
194
+ }, [core.sort]);
195
+ const filterValue = (0, import_react.useMemo)(() => {
196
+ const fv = {};
197
+ Object.entries(core.appliedFilters).forEach(([k, v]) => {
198
+ fv[k] = v;
199
+ });
200
+ return Object.keys(fv).length > 0 ? fv : void 0;
201
+ }, [core.appliedFilters]);
202
+ const handleSortChange = (0, import_react.useCallback)(
203
+ (sort) => {
204
+ const info = Array.isArray(sort) ? sort[0] : sort;
205
+ if (!info)
206
+ return;
207
+ const field = info.sortBy;
208
+ const currentDir = info.descending ? "desc" : "asc";
209
+ core.toggleSort(field);
210
+ },
211
+ [core]
212
+ );
213
+ const handleFilterChange = (0, import_react.useCallback)(
214
+ (filterVal) => {
215
+ core.applyFilters(filterVal);
216
+ },
217
+ [core]
218
+ );
219
+ const pagination = (0, import_react.useMemo)(() => {
220
+ if (!pageable)
221
+ return void 0;
222
+ return {
223
+ total: core.total,
224
+ current: core.page,
225
+ pageSize: core.pageSize,
226
+ pageSizeOptions,
227
+ showJumper: true,
228
+ showPageSize: true,
229
+ onChange: (pageInfo) => {
230
+ const { current, pageSize: size2 } = pageInfo;
231
+ if (current !== core.page)
232
+ core.setPage(current);
233
+ if (size2 !== core.pageSize)
234
+ core.setPageSize(size2);
235
+ }
236
+ };
237
+ }, [
238
+ pageable,
239
+ core.total,
240
+ core.page,
241
+ core.pageSize,
242
+ pageSizeOptions,
243
+ core.setPage,
244
+ core.setPageSize
245
+ ]);
246
+ const isLoading = typeof controlledLoading === "boolean" ? controlledLoading : core.status === "loading" || core.status === "refreshing";
247
+ const emptyContent = (0, import_react.useMemo)(() => {
248
+ if (core.status === "error") {
249
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-table-empty", children: [
250
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-icon", children: "⚠️" }),
251
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-desc", children: core.error || "加载失败" })
252
+ ] });
253
+ }
254
+ if (core.status === "no-permission") {
255
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-table-empty", children: [
256
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-icon", children: "🔒" }),
257
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-desc", children: "无权限查看" })
258
+ ] });
259
+ }
260
+ if (core.status === "empty-with-filter") {
261
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-table-empty", children: [
262
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-icon", children: "📭" }),
263
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-desc", children: emptyText }),
264
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-hint", children: emptyWithFilterHint })
265
+ ] });
266
+ }
267
+ if (core.status === "empty") {
268
+ if (empty)
269
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: empty });
270
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-table-empty", children: [
271
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-icon", children: "📭" }),
272
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-table-empty-desc", children: emptyText })
273
+ ] });
274
+ }
275
+ return void 0;
276
+ }, [core.status, core.error, empty, emptyText, emptyWithFilterHint]);
277
+ const handleSelectChange = (0, import_react.useCallback)(
278
+ (selectedRowKeys) => {
279
+ if (!isSelectable)
280
+ return;
281
+ const currentPageKeys = core.data.map((r) => String(r[rowKey]));
282
+ const disabledKeys = disabledRowKeysFn ? core.data.filter(disabledRowKeysFn).map((r) => String(r[rowKey])) : [];
283
+ const disabledSet = new Set(disabledKeys);
284
+ let keys = selectedRowKeys.map(String).filter((k) => !disabledSet.has(k));
285
+ if (effectiveCurrentPageOnly) {
286
+ keys = keys.filter((k) => currentPageKeys.includes(k));
287
+ }
288
+ if (controlledSelectedRowKeys === void 0) {
289
+ setInternalSelectedRowKeys(keys);
290
+ }
291
+ const selectedRows = keys.map(
292
+ (k) => {
293
+ var _a2;
294
+ return (_a2 = core.data.find((r) => String(r[rowKey]) === k)) != null ? _a2 : { [rowKey]: k };
295
+ }
296
+ );
297
+ onSelectionChange == null ? void 0 : onSelectionChange(selectedRows, {
298
+ tableId,
299
+ pageKeys: currentPageKeys,
300
+ disabledKeys,
301
+ trigger: "select"
302
+ });
303
+ },
304
+ [
305
+ isSelectable,
306
+ core.data,
307
+ rowKey,
308
+ disabledRowKeysFn,
309
+ effectiveCurrentPageOnly,
310
+ controlledSelectedRowKeys,
311
+ tableId,
312
+ onSelectionChange
313
+ ]
314
+ );
315
+ const scroll = (0, import_react.useMemo)(() => {
316
+ if (!bodyMaxHeight)
317
+ return void 0;
318
+ return { y: bodyMaxHeight };
319
+ }, [bodyMaxHeight]);
320
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
321
+ "div",
322
+ {
323
+ "data-theme": theme,
324
+ className: `pnt-table ${className != null ? className : ""}`,
325
+ style,
326
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
327
+ import_tdesign_react.Table,
328
+ {
329
+ rowKey,
330
+ data: core.data,
331
+ columns: tdesignCols,
332
+ loading: isLoading,
333
+ hover: true,
334
+ stripe: false,
335
+ bordered,
336
+ verticalAlign: "middle",
337
+ size,
338
+ defaultSort: defaultSortInfo,
339
+ filterValue,
340
+ loadingProps: { text: "加载中…" },
341
+ pagination,
342
+ scroll,
343
+ empty: emptyContent,
344
+ selectedRowKeys: isSelectable ? controlledSelectedRowKeys != null ? controlledSelectedRowKeys : internalSelectedRowKeys : void 0,
345
+ onSortChange: handleSortChange,
346
+ onFilterChange: handleFilterChange,
347
+ onSelectChange: handleSelectChange,
348
+ rowClassName
349
+ }
350
+ )
351
+ }
352
+ );
353
+ });
354
+ PntTable.displayName = "PntTable";
355
+ var PntTable_default = PntTable;
356
+ // Annotate the CommonJS export names for ESM import in node:
357
+ 0 && (module.exports = {
358
+ PntTable
359
+ });
@@ -0,0 +1,315 @@
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
+ .pnt-table .t-table__cell--fixed-left,
243
+ .pnt-table .t-table__cell--fixed-right {
244
+ background-color: var(--pnt-table-bg);
245
+ box-shadow: none;
246
+
247
+ // 固定列阴影边界
248
+ &::after {
249
+ content: '';
250
+ position: absolute;
251
+ top: 0;
252
+ bottom: 0;
253
+ width: 8px;
254
+ pointer-events: none;
255
+ }
256
+ }
257
+
258
+ .pnt-table .t-table__cell--fixed-left::after {
259
+ right: -8px;
260
+ box-shadow: inset -8px 0 4px -4px rgba(0, 0, 0, 0.06);
261
+ }
262
+
263
+ .pnt-table .t-table__cell--fixed-right::after {
264
+ left: -8px;
265
+ box-shadow: inset 8px 0 4px -4px rgba(0, 0, 0, 0.06);
266
+ }
267
+
268
+ // 固定列跟随 hover/选中背景
269
+ .pnt-table .t-table__body tr:hover > td.t-table__cell--fixed-left,
270
+ .pnt-table .t-table__body tr:hover > td.t-table__cell--fixed-right {
271
+ background-color: var(--pnt-table-bg-hover);
272
+ }
273
+
274
+ .pnt-table .t-table__body tr.is-selected > td.t-table__cell--fixed-left,
275
+ .pnt-table .t-table__body tr.is-selected > td.t-table__cell--fixed-right {
276
+ background-color: var(--pnt-table-bg-selected);
277
+ }
278
+
279
+ // ============================================================
280
+ // 多选列(TBL-06.01 / TBL-06.02)
281
+ // ============================================================
282
+
283
+ .pnt-table .t-table__select {
284
+ width: 44px;
285
+ text-align: center;
286
+
287
+ .t-checkbox {
288
+ cursor: pointer;
289
+ }
290
+ }
291
+
292
+ // ============================================================
293
+ // 滚动容器
294
+ // ============================================================
295
+
296
+ .pnt-table .t-table__body-wrapper::-webkit-scrollbar,
297
+ .pnt-table .t-table__header-wrapper::-webkit-scrollbar {
298
+ width: 8px;
299
+ height: 8px;
300
+ }
301
+
302
+ .pnt-table .t-table__body-wrapper::-webkit-scrollbar-thumb,
303
+ .pnt-table .t-table__header-wrapper::-webkit-scrollbar-thumb {
304
+ background: #c9cdd4;
305
+ border-radius: 4px;
306
+
307
+ &:hover {
308
+ background: var(--pnt-table-brand);
309
+ }
310
+ }
311
+
312
+ .pnt-table .t-table__body-wrapper::-webkit-scrollbar-track,
313
+ .pnt-table .t-table__header-wrapper::-webkit-scrollbar-track {
314
+ background: transparent;
315
+ }