@pnt-team/pnt-component-frontend 0.0.73 → 0.0.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -10
- package/lib/cjs/components/PntBusinessSelect/index.js +5 -0
- package/lib/cjs/components/PntBusinessSelect/index.scss +19 -0
- package/lib/esm/api/index.js +13 -10
- package/lib/esm/api/types/index.js +1 -0
- package/lib/esm/components/PntBusinessSelect/index.js +252 -195
- package/lib/esm/components/PntBusinessSelect/index.scss +19 -0
- package/lib/esm/components/PntGeneralLayout/index.js +74 -40
- package/lib/esm/components/PntImagePreview/index.js +111 -59
- package/lib/esm/components/PntSecureBox/index.js +63 -26
- package/lib/esm/config.js +33 -13
- package/lib/esm/hooks/useHighSecurity.js +29 -25
- package/lib/esm/index.js +18 -17
- package/lib/esm/utils/const.js +14 -17
- package/lib/esm/utils/index.js +15 -12
- package/lib/umd/pnt-component-frontend.min.css +1 -1
- package/lib/umd/pnt-component-frontend.min.js +1 -1
- package/package.json +1 -1
|
@@ -1,26 +1,71 @@
|
|
|
1
|
-
// src/components/PntBusinessSelect/index.tsx
|
|
2
1
|
import { businessSelectList } from "../../api";
|
|
3
2
|
import { usePntConfig } from "../../config";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
useEffect,
|
|
7
|
-
useMemo,
|
|
8
|
-
useRef,
|
|
9
|
-
useState
|
|
10
|
-
} from "react";
|
|
11
|
-
import { Loading, Select, Tooltip } from "tdesign-react";
|
|
3
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { Loading, Select, Tooltip } from 'tdesign-react';
|
|
12
5
|
import "./index.scss";
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
|
|
7
|
+
// ==========================================
|
|
8
|
+
// 类型定义
|
|
9
|
+
// ==========================================
|
|
10
|
+
|
|
11
|
+
/** 下拉场景 */
|
|
12
|
+
|
|
13
|
+
/** 标签格式 */
|
|
14
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
15
|
+
/**
|
|
16
|
+
* 业务选择器组件
|
|
17
|
+
* @description 统一业务下拉选择器,内置对接后端通用接口 `/api/business/select/list`,
|
|
18
|
+
* 支持远程搜索、滚动分页加载、多选、场景化禁用(合规场景 creatable 字段)。
|
|
19
|
+
*
|
|
20
|
+
* @param {PntBusinessSelectProps} props - 组件属性
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* import { PntBusinessSelect } from '@pnt-team/pnt-component-frontend';
|
|
25
|
+
*
|
|
26
|
+
* // 最简用法
|
|
27
|
+
* <PntBusinessSelect
|
|
28
|
+
* value={gameId}
|
|
29
|
+
* onChange={(v) => setGameId(v as string)}
|
|
30
|
+
* />
|
|
31
|
+
*
|
|
32
|
+
* // SDK 定制场景 + 名称带 ID
|
|
33
|
+
* <PntBusinessSelect
|
|
34
|
+
* scene="sdk"
|
|
35
|
+
* labelFormat="name_id"
|
|
36
|
+
* value={gameId}
|
|
37
|
+
* onChange={(v) => setGameId(v as string)}
|
|
38
|
+
* />
|
|
39
|
+
*
|
|
40
|
+
* // 合规年龄场景(自动禁用 creatable=false 的业务)
|
|
41
|
+
* <PntBusinessSelect
|
|
42
|
+
* scene="compliance_age"
|
|
43
|
+
* value={gameId}
|
|
44
|
+
* onChange={(v) => setGameId(v as string)}
|
|
45
|
+
* />
|
|
46
|
+
*
|
|
47
|
+
* // 多选 + 状态过滤
|
|
48
|
+
* <PntBusinessSelect
|
|
49
|
+
* multiple
|
|
50
|
+
* statuses={[3, 4]}
|
|
51
|
+
* labelFormat="name_id"
|
|
52
|
+
* value={gameIds}
|
|
53
|
+
* onChange={(v) => setGameIds(v as string[])}
|
|
54
|
+
* />
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @returns {ReactElement} 业务选择器组件
|
|
58
|
+
*/
|
|
59
|
+
const PntBusinessSelect = props => {
|
|
15
60
|
const {
|
|
16
61
|
value,
|
|
17
62
|
onChange,
|
|
18
63
|
multiple = false,
|
|
19
|
-
scene =
|
|
64
|
+
scene = 'default',
|
|
20
65
|
statuses,
|
|
21
66
|
businessTypes,
|
|
22
67
|
businessAccessMethod,
|
|
23
|
-
labelFormat =
|
|
68
|
+
labelFormat = 'name',
|
|
24
69
|
labelField,
|
|
25
70
|
optionLabel,
|
|
26
71
|
pagination = true,
|
|
@@ -37,152 +82,149 @@ var PntBusinessSelect = (props) => {
|
|
|
37
82
|
fields,
|
|
38
83
|
optionDisabled,
|
|
39
84
|
optionTooltip,
|
|
40
|
-
tooltipPlacement =
|
|
85
|
+
tooltipPlacement = 'top',
|
|
41
86
|
className,
|
|
42
87
|
style
|
|
43
88
|
} = props;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
);
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
89
|
+
|
|
90
|
+
// ---- 内置请求函数(通过 PntProvider 注入的 http 实例) ----
|
|
91
|
+
const {
|
|
92
|
+
http
|
|
93
|
+
} = usePntConfig();
|
|
94
|
+
const requestFn = useCallback(params => businessSelectList(http, params), [http]);
|
|
95
|
+
|
|
96
|
+
// ---- 状态 ----
|
|
97
|
+
const [options, setOptions] = useState([]);
|
|
52
98
|
const [loading, setLoading] = useState(false);
|
|
53
|
-
const [keyword, setKeyword] = useState(
|
|
99
|
+
const [keyword, setKeyword] = useState('');
|
|
54
100
|
const [pageNum, setPageNum] = useState(1);
|
|
55
101
|
const [total, setTotal] = useState(0);
|
|
102
|
+
|
|
103
|
+
// ---- Refs ----
|
|
56
104
|
const debounceRef = useRef();
|
|
57
105
|
const loadingRef = useRef(false);
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
setLoading(true);
|
|
87
|
-
try {
|
|
88
|
-
const res = await requestFn(buildParams(kw, page));
|
|
89
|
-
if (res.code === 0 && res.data) {
|
|
90
|
-
const newList = res.data.list || [];
|
|
91
|
-
setOptions((prev) => append ? [...prev, ...newList] : newList);
|
|
92
|
-
setTotal(((_a = res.data.pagination) == null ? void 0 : _a.total) || 0);
|
|
93
|
-
setPageNum(page);
|
|
94
|
-
}
|
|
95
|
-
} finally {
|
|
96
|
-
loadingRef.current = false;
|
|
97
|
-
setLoading(false);
|
|
106
|
+
/** 已选中项的完整数据缓存,确保搜索后选项仍能展示 label */
|
|
107
|
+
const selectedItemsRef = useRef(new Map());
|
|
108
|
+
|
|
109
|
+
// ---- 构建请求参数 ----
|
|
110
|
+
const buildParams = useCallback((kw, page) => ({
|
|
111
|
+
keyword: kw || undefined,
|
|
112
|
+
pageNum: pagination ? page : 1,
|
|
113
|
+
pageSize: pagination ? pageSize : 0,
|
|
114
|
+
scene,
|
|
115
|
+
statuses,
|
|
116
|
+
business_types: businessTypes,
|
|
117
|
+
business_access_method: businessAccessMethod,
|
|
118
|
+
fields
|
|
119
|
+
}), [scene, statuses, businessTypes, businessAccessMethod, pageSize, fields, pagination]);
|
|
120
|
+
|
|
121
|
+
// ---- 获取数据 ----
|
|
122
|
+
const fetchData = useCallback(async (kw, page, append) => {
|
|
123
|
+
if (loadingRef.current) return;
|
|
124
|
+
loadingRef.current = true;
|
|
125
|
+
setLoading(true);
|
|
126
|
+
try {
|
|
127
|
+
const res = await requestFn(buildParams(kw, page));
|
|
128
|
+
// 适配 commonEesType:code === 0 视为成功
|
|
129
|
+
if (res.code === 0 && res.data) {
|
|
130
|
+
const newList = res.data.list || [];
|
|
131
|
+
setOptions(prev => append ? [...prev, ...newList] : newList);
|
|
132
|
+
setTotal(res.data.pagination?.total || 0);
|
|
133
|
+
setPageNum(page);
|
|
98
134
|
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
);
|
|
135
|
+
} finally {
|
|
136
|
+
loadingRef.current = false;
|
|
137
|
+
setLoading(false);
|
|
138
|
+
}
|
|
139
|
+
}, [requestFn, buildParams]);
|
|
140
|
+
|
|
141
|
+
// ---- 远程搜索(防抖) ----
|
|
142
|
+
const handleSearch = useCallback(val => {
|
|
143
|
+
setKeyword(val);
|
|
144
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
145
|
+
debounceRef.current = setTimeout(() => {
|
|
146
|
+
fetchData(val, 1, false);
|
|
147
|
+
}, searchDebounce);
|
|
148
|
+
}, [fetchData, searchDebounce]);
|
|
149
|
+
|
|
150
|
+
// ---- 滚动触底加载下一页 ----
|
|
113
151
|
const handleScrollToBottom = useCallback(() => {
|
|
114
152
|
if (!loadingRef.current && options.length < total) {
|
|
115
153
|
fetchData(keyword, pageNum + 1, true);
|
|
116
154
|
}
|
|
117
155
|
}, [options.length, total, keyword, pageNum, fetchData]);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
156
|
+
|
|
157
|
+
// ---- 弹窗关闭时重置搜索 ----
|
|
158
|
+
const handlePopupVisibleChange = useCallback(visible => {
|
|
159
|
+
if (!visible && keyword) {
|
|
160
|
+
setKeyword('');
|
|
161
|
+
fetchData('', 1, false);
|
|
162
|
+
}
|
|
163
|
+
}, [keyword, fetchData]);
|
|
164
|
+
|
|
165
|
+
// ---- 初始加载 + 筛选条件变化时重新加载 ----
|
|
127
166
|
useEffect(() => {
|
|
128
|
-
fetchData(
|
|
167
|
+
fetchData('', 1, false);
|
|
129
168
|
}, [fetchData]);
|
|
169
|
+
|
|
170
|
+
// ---- 缓存已选中项(确保搜索后仍能展示 label) ----
|
|
130
171
|
useEffect(() => {
|
|
131
172
|
const selectedValues = Array.isArray(value) ? value : value ? [value] : [];
|
|
132
|
-
options.forEach(
|
|
173
|
+
options.forEach(item => {
|
|
133
174
|
if (item.game_id && selectedValues.includes(item.game_id)) {
|
|
134
175
|
selectedItemsRef.current.set(item.game_id, item);
|
|
135
176
|
}
|
|
136
177
|
});
|
|
137
178
|
}, [options, value]);
|
|
179
|
+
|
|
180
|
+
// ---- 组件卸载时清理防抖 ----
|
|
138
181
|
useEffect(() => {
|
|
139
182
|
return () => {
|
|
140
|
-
if (debounceRef.current)
|
|
141
|
-
clearTimeout(debounceRef.current);
|
|
183
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
142
184
|
};
|
|
143
185
|
}, []);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
[optionTooltip]
|
|
182
|
-
);
|
|
186
|
+
|
|
187
|
+
// ---- 格式化标签 ----
|
|
188
|
+
const formatLabel = useCallback(item => {
|
|
189
|
+
// 优先级:optionLabel > labelField > labelFormat
|
|
190
|
+
if (optionLabel) return optionLabel(item);
|
|
191
|
+
if (labelField) {
|
|
192
|
+
return item[labelField] || item.name || '';
|
|
193
|
+
}
|
|
194
|
+
const name = item.name || '';
|
|
195
|
+
const nameEn = item.name_en || '';
|
|
196
|
+
const gameId = item.game_id || '';
|
|
197
|
+
switch (labelFormat) {
|
|
198
|
+
case 'name_en':
|
|
199
|
+
return nameEn || name;
|
|
200
|
+
case 'name_id':
|
|
201
|
+
return `${name}(${gameId})`;
|
|
202
|
+
case 'name_en_id':
|
|
203
|
+
return `${nameEn}(${gameId})`;
|
|
204
|
+
default:
|
|
205
|
+
return name;
|
|
206
|
+
}
|
|
207
|
+
}, [labelFormat, labelField, optionLabel]);
|
|
208
|
+
|
|
209
|
+
// ---- 判断选项是否禁用(合规场景 creatable + 自定义 optionDisabled 叠加) ----
|
|
210
|
+
const isOptionDisabled = useCallback(item => {
|
|
211
|
+
if (scene === 'compliance_age' || scene === 'custom_region') {
|
|
212
|
+
if (item.creatable === false) return true;
|
|
213
|
+
}
|
|
214
|
+
return optionDisabled ? optionDisabled(item) : false;
|
|
215
|
+
}, [scene, optionDisabled]);
|
|
216
|
+
|
|
217
|
+
// ---- 计算选项 tooltip 内容 ----
|
|
218
|
+
const getOptionTooltip = useCallback(item => {
|
|
219
|
+
return optionTooltip ? optionTooltip(item) : '';
|
|
220
|
+
}, [optionTooltip]);
|
|
221
|
+
|
|
222
|
+
// ---- 构建最终选项(当前页 + 已选中项补充) ----
|
|
183
223
|
const selectOptions = useMemo(() => {
|
|
184
|
-
const map =
|
|
185
|
-
|
|
224
|
+
const map = new Map();
|
|
225
|
+
|
|
226
|
+
// 当前页选项
|
|
227
|
+
options.forEach(item => {
|
|
186
228
|
if (item.game_id) {
|
|
187
229
|
map.set(item.game_id, {
|
|
188
230
|
label: formatLabel(item),
|
|
@@ -195,8 +237,10 @@ var PntBusinessSelect = (props) => {
|
|
|
195
237
|
});
|
|
196
238
|
}
|
|
197
239
|
});
|
|
240
|
+
|
|
241
|
+
// 已选中项(搜索结果不含时补充,确保 label 始终可展示)
|
|
198
242
|
const selectedValues = Array.isArray(value) ? value : value ? [value] : [];
|
|
199
|
-
selectedValues.forEach(
|
|
243
|
+
selectedValues.forEach(id => {
|
|
200
244
|
if (!map.has(id)) {
|
|
201
245
|
const item = selectedItemsRef.current.get(id);
|
|
202
246
|
if (item) {
|
|
@@ -210,75 +254,88 @@ var PntBusinessSelect = (props) => {
|
|
|
210
254
|
rawItem: item
|
|
211
255
|
});
|
|
212
256
|
} else {
|
|
213
|
-
|
|
257
|
+
// 降级:无完整数据时显示原始 ID
|
|
258
|
+
map.set(id, {
|
|
259
|
+
label: id,
|
|
260
|
+
value: id,
|
|
261
|
+
disabled: false
|
|
262
|
+
});
|
|
214
263
|
}
|
|
215
264
|
}
|
|
216
265
|
});
|
|
217
266
|
return Array.from(map.values());
|
|
218
267
|
}, [options, value, formatLabel, isOptionDisabled]);
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
268
|
+
|
|
269
|
+
// ---- 本地过滤(与后端 keyword 逻辑一致:name / name_en / game_id) ----
|
|
270
|
+
const localFilter = useCallback((filterWord, option) => {
|
|
271
|
+
if (!filterWord) return true;
|
|
272
|
+
const kw = filterWord.toLowerCase();
|
|
273
|
+
const opt = option;
|
|
274
|
+
// 优先按 rawItem 的所有字符串字段匹配(含 labelField 指定字段)
|
|
275
|
+
const item = opt?.rawItem;
|
|
276
|
+
if (item) {
|
|
277
|
+
return Object.values(item).some(v => typeof v === 'string' && v.toLowerCase().includes(kw));
|
|
278
|
+
}
|
|
279
|
+
return (opt?.name || '').toLowerCase().includes(kw) || (opt?.name_en || '').toLowerCase().includes(kw) || (opt?.game_id || '').includes(kw);
|
|
280
|
+
}, []);
|
|
281
|
+
const {
|
|
282
|
+
Option
|
|
283
|
+
} = Select;
|
|
284
|
+
|
|
285
|
+
// 是否启用 tooltip(仅当 optionTooltip 传入时生效)
|
|
236
286
|
const hasTooltip = !!optionTooltip;
|
|
287
|
+
|
|
288
|
+
// 一次性加载模式:仅本地过滤,不触发远程搜索;不分页时禁用滚动触底
|
|
237
289
|
const enableRemoteSearch = filterable && pagination;
|
|
238
290
|
const enableScrollBottom = pagination;
|
|
239
|
-
return
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
291
|
+
return /*#__PURE__*/_jsx(Select, {
|
|
292
|
+
value: value,
|
|
293
|
+
onChange: onChange,
|
|
294
|
+
filterable: filterable,
|
|
295
|
+
filter: filterable ? localFilter : undefined,
|
|
296
|
+
onSearch: enableRemoteSearch ? handleSearch : undefined,
|
|
297
|
+
loading: loading && options.length === 0,
|
|
298
|
+
multiple: multiple,
|
|
299
|
+
max: max,
|
|
300
|
+
clearable: clearable,
|
|
301
|
+
disabled: disabled,
|
|
302
|
+
placeholder: placeholder,
|
|
303
|
+
minCollapsedNum: minCollapsedNum,
|
|
304
|
+
collapsedItems: collapsedItems,
|
|
305
|
+
onPopupVisibleChange: handlePopupVisibleChange,
|
|
306
|
+
panelBottomContent: options.length > 0 && loading ? /*#__PURE__*/_jsx(Loading, {
|
|
307
|
+
loading: true,
|
|
308
|
+
size: "small"
|
|
309
|
+
}) : null,
|
|
310
|
+
popupProps: {
|
|
311
|
+
...popupProps,
|
|
312
|
+
// 自动注入弹层样式类,消费方无需重复定义 overlayClassName
|
|
313
|
+
overlayClassName: ['pnt-business-select-popup', popupProps?.overlayClassName].filter(Boolean).join(' '),
|
|
314
|
+
onScrollToBottom: enableScrollBottom ? handleScrollToBottom : undefined
|
|
315
|
+
},
|
|
316
|
+
className: className ? `pnt-business-select ${className}` : 'pnt-business-select',
|
|
317
|
+
style: style,
|
|
318
|
+
children: selectOptions.map(opt => {
|
|
319
|
+
const item = opt.rawItem;
|
|
320
|
+
const tip = hasTooltip && item ? getOptionTooltip(item) : '';
|
|
321
|
+
// label 仅用纯文本字符串(用于 tag 显示与过滤匹配),复杂渲染走 children
|
|
322
|
+
const labelText = typeof opt.label === 'string' ? opt.label : String(opt.label ?? '');
|
|
323
|
+
// Tooltip 必须放在 Option 内部:Select 通过直接子节点识别 Option,
|
|
324
|
+
// 外层包裹会破坏选项注册;且禁用项的 hover 事件由内部元素接收才稳定
|
|
325
|
+
return /*#__PURE__*/_jsx(Option, {
|
|
326
|
+
value: opt.value,
|
|
327
|
+
label: labelText,
|
|
328
|
+
disabled: opt.disabled,
|
|
329
|
+
children: tip ? /*#__PURE__*/_jsx(Tooltip, {
|
|
330
|
+
content: tip,
|
|
331
|
+
placement: tooltipPlacement,
|
|
332
|
+
children: /*#__PURE__*/_jsx("span", {
|
|
333
|
+
className: "pnt-business-select-option-label",
|
|
334
|
+
children: opt.label
|
|
335
|
+
})
|
|
336
|
+
}) : opt.label
|
|
337
|
+
}, opt.value);
|
|
338
|
+
})
|
|
339
|
+
});
|
|
284
340
|
};
|
|
341
|
+
export default PntBusinessSelect;
|
|
@@ -6,3 +6,22 @@
|
|
|
6
6
|
display: block;
|
|
7
7
|
width: 100%;
|
|
8
8
|
}
|
|
9
|
+
|
|
10
|
+
/* 弹层内容样式:loading / total-wrapper 统一高度与排版 */
|
|
11
|
+
.pnt-business-select-popup {
|
|
12
|
+
.t-popup__content {
|
|
13
|
+
.total-wrapper,
|
|
14
|
+
.t-loading {
|
|
15
|
+
height: 36px;
|
|
16
|
+
font-weight: 400;
|
|
17
|
+
line-height: 20px;
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
padding: 0 14px;
|
|
21
|
+
}
|
|
22
|
+
.total-wrapper {
|
|
23
|
+
color: #808191;
|
|
24
|
+
font-size: 14px;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -1,9 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
import { ArrowLeftIcon } from
|
|
3
|
-
import { Layout } from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ArrowLeftIcon } from 'tdesign-icons-react';
|
|
3
|
+
import { Layout } from 'tdesign-react';
|
|
4
4
|
import "./index.scss";
|
|
5
|
-
import { jsx
|
|
6
|
-
|
|
5
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
6
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
|
+
/**
|
|
8
|
+
* 通用页面布局组件(L3 布局组件)
|
|
9
|
+
* @description 提供标准的「头部 + 子头部 + 内容区 + 底部」页面布局结构,
|
|
10
|
+
* 基于 TDesign Layout 封装,适用于后台管理系统的详情页、表单页等场景。
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* // 基础用法:仅内容区
|
|
15
|
+
* <PntGeneralLayout>
|
|
16
|
+
* <div>页面内容</div>
|
|
17
|
+
* </PntGeneralLayout>
|
|
18
|
+
*
|
|
19
|
+
* // 带标题和操作按钮
|
|
20
|
+
* <PntGeneralLayout
|
|
21
|
+
* headerTitle="页面标题"
|
|
22
|
+
* operation={<><Button>保存</Button><Button>取消</Button></>}
|
|
23
|
+
* >
|
|
24
|
+
* <Form />
|
|
25
|
+
* </PntGeneralLayout>
|
|
26
|
+
*
|
|
27
|
+
* // 带返回按钮和底部
|
|
28
|
+
* <PntGeneralLayout
|
|
29
|
+
* headerTitle="详情页"
|
|
30
|
+
* showBackButton
|
|
31
|
+
* onBackButtonClick={() => history.back()}
|
|
32
|
+
* footer={<><Button>提交</Button><Button>重置</Button></>}
|
|
33
|
+
* >
|
|
34
|
+
* <Detail />
|
|
35
|
+
* </PntGeneralLayout>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
const PntGeneralLayout = prop => {
|
|
7
39
|
const {
|
|
8
40
|
className,
|
|
9
41
|
headerTitle,
|
|
@@ -16,39 +48,41 @@ var PntGeneralLayout = (prop) => {
|
|
|
16
48
|
bodyStyle,
|
|
17
49
|
footer
|
|
18
50
|
} = prop;
|
|
19
|
-
const {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
const {
|
|
52
|
+
Header,
|
|
53
|
+
Footer
|
|
54
|
+
} = Layout;
|
|
55
|
+
return /*#__PURE__*/_jsxs(Layout, {
|
|
56
|
+
className: `pnt-general-layout${className ? ' ' + className : ''}`,
|
|
57
|
+
children: [headerTitle && /*#__PURE__*/_jsxs(Header, {
|
|
58
|
+
children: [/*#__PURE__*/_jsxs("span", {
|
|
59
|
+
className: "header-title",
|
|
60
|
+
children: [showBackButton && /*#__PURE__*/_jsx(ArrowLeftIcon, {
|
|
61
|
+
className: "goback-icon",
|
|
62
|
+
size: "24px",
|
|
63
|
+
onClick: onBackButtonClick
|
|
64
|
+
}), /*#__PURE__*/_jsx("span", {
|
|
65
|
+
className: "title",
|
|
66
|
+
children: headerTitle
|
|
67
|
+
})]
|
|
68
|
+
}), operation && /*#__PURE__*/_jsx("span", {
|
|
69
|
+
className: "header-operation",
|
|
70
|
+
children: operation
|
|
71
|
+
})]
|
|
72
|
+
}), subHeaderContent && /*#__PURE__*/_jsx("div", {
|
|
73
|
+
className: "general-layout-sub-header",
|
|
74
|
+
children: subHeaderContent
|
|
75
|
+
}), /*#__PURE__*/_jsx("div", {
|
|
76
|
+
className: "general-layout-body",
|
|
77
|
+
ref: bodyRef,
|
|
78
|
+
style: {
|
|
79
|
+
height: `calc(100%${headerTitle && ' - 72px'}${footer && ' - 72px'})`,
|
|
80
|
+
...bodyStyle
|
|
81
|
+
},
|
|
82
|
+
children: children
|
|
83
|
+
}), footer && /*#__PURE__*/_jsx(Footer, {
|
|
84
|
+
children: footer
|
|
85
|
+
})]
|
|
86
|
+
});
|
|
54
87
|
};
|
|
88
|
+
export default PntGeneralLayout;
|