@pnt-team/pnt-component-frontend 0.0.88 → 0.0.91
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/lib/cjs/components/PntBusinessSelect/index.js +17 -9
- package/lib/cjs/components/PntGeneralLayout/index.d.ts +33 -1
- package/lib/cjs/components/PntGeneralLayout/index.js +16 -13
- package/lib/cjs/components/PntGeneralLayout/index.scss +121 -90
- package/lib/cjs/components/PntImage/index.d.ts +87 -0
- package/lib/cjs/components/PntImage/index.js +137 -0
- package/lib/{esm/components/PntImagePreview → cjs/components/PntImage}/index.scss +14 -7
- package/lib/cjs/index.d.ts +1 -1
- package/lib/cjs/index.js +3 -3
- package/lib/esm/components/PntBusinessSelect/index.js +27 -19
- package/lib/esm/components/PntGeneralLayout/index.d.ts +33 -1
- package/lib/esm/components/PntGeneralLayout/index.js +21 -6
- package/lib/esm/components/PntGeneralLayout/index.scss +121 -90
- package/lib/esm/components/PntImage/index.d.ts +87 -0
- package/lib/esm/components/PntImage/index.js +181 -0
- package/lib/{cjs/components/PntImagePreview → esm/components/PntImage}/index.scss +14 -7
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.js +1 -1
- 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
- package/lib/cjs/components/PntImagePreview/index.d.ts +0 -75
- package/lib/cjs/components/PntImagePreview/index.js +0 -98
- package/lib/esm/components/PntImagePreview/index.d.ts +0 -75
- package/lib/esm/components/PntImagePreview/index.js +0 -120
package/lib/cjs/index.js
CHANGED
|
@@ -31,14 +31,14 @@ var src_exports = {};
|
|
|
31
31
|
__export(src_exports, {
|
|
32
32
|
PntBusinessSelect: () => import_PntBusinessSelect.default,
|
|
33
33
|
PntGeneralLayout: () => import_PntGeneralLayout.default,
|
|
34
|
-
|
|
34
|
+
PntImage: () => import_PntImage.default,
|
|
35
35
|
PntProvider: () => import_config.PntProvider,
|
|
36
36
|
PntSecureBox: () => import_PntSecureBox.default,
|
|
37
37
|
usePntConfig: () => import_config.usePntConfig
|
|
38
38
|
});
|
|
39
39
|
module.exports = __toCommonJS(src_exports);
|
|
40
40
|
var import_config = require("./config");
|
|
41
|
-
var
|
|
41
|
+
var import_PntImage = __toESM(require("./components/PntImage"));
|
|
42
42
|
var import_PntBusinessSelect = __toESM(require("./components/PntBusinessSelect"));
|
|
43
43
|
var import_PntSecureBox = __toESM(require("./components/PntSecureBox"));
|
|
44
44
|
var import_PntGeneralLayout = __toESM(require("./components/PntGeneralLayout"));
|
|
@@ -46,7 +46,7 @@ var import_PntGeneralLayout = __toESM(require("./components/PntGeneralLayout"));
|
|
|
46
46
|
0 && (module.exports = {
|
|
47
47
|
PntBusinessSelect,
|
|
48
48
|
PntGeneralLayout,
|
|
49
|
-
|
|
49
|
+
PntImage,
|
|
50
50
|
PntProvider,
|
|
51
51
|
PntSecureBox,
|
|
52
52
|
usePntConfig
|
|
@@ -153,8 +153,6 @@ const PntBusinessSelect = props => {
|
|
|
153
153
|
// append=true 为滚动翻页(追加),append=false 为初始化/搜索/重置(替换)
|
|
154
154
|
const fetchData = useCallback(async (kw, page, append) => {
|
|
155
155
|
if (append) {
|
|
156
|
-
// 任何请求进行中都不再追加;并校验关键词/页码/总数,
|
|
157
|
-
// 防止搜索后残余滚动事件用新关键词 + 旧页码请求错误分页
|
|
158
156
|
if (appendLoadingRef.current || loadingRef.current) return;
|
|
159
157
|
const meta = listMetaRef.current;
|
|
160
158
|
if (meta.keyword !== kw) return;
|
|
@@ -162,26 +160,21 @@ const PntBusinessSelect = props => {
|
|
|
162
160
|
if (optionsRef.current.length >= meta.total) return;
|
|
163
161
|
appendLoadingRef.current = true;
|
|
164
162
|
}
|
|
165
|
-
// 替换请求不被在途请求阻塞,靠序号让旧响应失效
|
|
166
163
|
const seq = ++fetchSeqRef.current;
|
|
167
164
|
loadingRef.current = true;
|
|
168
165
|
setLoading(true);
|
|
169
166
|
try {
|
|
170
167
|
const res = await requestFn(buildParams(kw, page));
|
|
171
|
-
// 已有更新的替换请求发出,本次(可能是旧关键词的翻页)结果丢弃
|
|
172
168
|
if (seq !== fetchSeqRef.current) return;
|
|
173
|
-
// 适配 commonEesType:code === 0 视为成功
|
|
174
169
|
if (res.code === 0 && res.data) {
|
|
175
170
|
const newList = res.data.list || [];
|
|
176
171
|
const resTotal = res.data.pagination?.total || 0;
|
|
177
172
|
let nextList;
|
|
178
173
|
if (append) {
|
|
179
|
-
// 按 game_id 去重,避免重复页混入
|
|
180
174
|
const existed = new Set(optionsRef.current.map(o => o.game_id));
|
|
181
175
|
nextList = [...optionsRef.current, ...newList.filter(o => !existed.has(o.game_id))];
|
|
182
176
|
} else {
|
|
183
177
|
nextList = newList;
|
|
184
|
-
// 列表整体替换,屏蔽随后浏览器 clamp 滚动位置触发的残余触底
|
|
185
178
|
lastReplaceAtRef.current = Date.now();
|
|
186
179
|
}
|
|
187
180
|
optionsRef.current = nextList;
|
|
@@ -193,6 +186,8 @@ const PntBusinessSelect = props => {
|
|
|
193
186
|
setOptions(nextList);
|
|
194
187
|
onLoadedRef.current?.(nextList);
|
|
195
188
|
}
|
|
189
|
+
} catch {
|
|
190
|
+
// 请求失败静默处理,不阻塞主流程
|
|
196
191
|
} finally {
|
|
197
192
|
if (append) appendLoadingRef.current = false;
|
|
198
193
|
if (seq === fetchSeqRef.current) {
|
|
@@ -207,7 +202,6 @@ const PntBusinessSelect = props => {
|
|
|
207
202
|
keywordRef.current = val;
|
|
208
203
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
209
204
|
debounceRef.current = setTimeout(() => {
|
|
210
|
-
// 防抖结束时以最新输入为准(请求序号会使期间的旧翻页响应失效)
|
|
211
205
|
fetchData(keywordRef.current, 1, false);
|
|
212
206
|
}, searchDebounce);
|
|
213
207
|
}, [fetchData, searchDebounce]);
|
|
@@ -224,8 +218,14 @@ const PntBusinessSelect = props => {
|
|
|
224
218
|
const handlePopupVisibleChange = useCallback(visible => {
|
|
225
219
|
if (visible) return;
|
|
226
220
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
227
|
-
|
|
228
|
-
|
|
221
|
+
// 必须以「当前列表实际使用的关键词」为准,不能只看 keywordRef:
|
|
222
|
+
// 1) 单选选中一项后,TDesign 关闭弹窗时会先以 blur 触发 onSearch(''),
|
|
223
|
+
// 把 keywordRef 提前清空并挂一个重置 timer,随后本回调又会清掉该 timer,
|
|
224
|
+
// 若仅按 keywordRef 判断就会漏发重置请求;
|
|
225
|
+
// 2) 用户手动清空输入但未关闭下拉时,keywordRef 已为 '',但列表仍残留
|
|
226
|
+
// 上次搜索结果,关闭时必须重置回默认列表。
|
|
227
|
+
keywordRef.current = '';
|
|
228
|
+
if (listMetaRef.current.keyword) {
|
|
229
229
|
fetchData('', 1, false);
|
|
230
230
|
}
|
|
231
231
|
}, [fetchData]);
|
|
@@ -429,11 +429,11 @@ const PntBusinessSelect = props => {
|
|
|
429
429
|
// 兜底:从 TDesign 上下文剥离 React 字段(children/content)
|
|
430
430
|
const fromCtx = ctxOptions.find(o => String(o.value) === id);
|
|
431
431
|
if (fromCtx) {
|
|
432
|
-
const {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
432
|
+
const rest = {
|
|
433
|
+
...fromCtx
|
|
434
|
+
};
|
|
435
|
+
delete rest.children;
|
|
436
|
+
delete rest.content;
|
|
437
437
|
return rest;
|
|
438
438
|
}
|
|
439
439
|
return {
|
|
@@ -446,15 +446,23 @@ const PntBusinessSelect = props => {
|
|
|
446
446
|
selectedOptions: multiple ? selectedOptions : selectedOptions.slice(0, 1)
|
|
447
447
|
});
|
|
448
448
|
}, [onChange, multiple, getLabelText]);
|
|
449
|
+
|
|
450
|
+
// 点击 clear 按钮(X)时重置列表——TDesign 的 onClearValue 只 setInputValue,
|
|
451
|
+
// 不触发 handleInputChange / onSearch,所以需要主动重置。
|
|
452
|
+
const handleClear = useCallback(() => {
|
|
453
|
+
keywordRef.current = '';
|
|
454
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
455
|
+
if (listMetaRef.current.keyword) {
|
|
456
|
+
fetchData('', 1, false);
|
|
457
|
+
}
|
|
458
|
+
}, [fetchData]);
|
|
449
459
|
return /*#__PURE__*/_jsx(Select, {
|
|
450
460
|
value: selectValue,
|
|
451
461
|
onChange: handleChange,
|
|
452
|
-
filterable: filterable
|
|
453
|
-
// 远程搜索模式由服务端过滤(传了 onSearch 时 TDesign 也会跳过本地过滤);
|
|
454
|
-
// 仅全量模式使用本地过滤
|
|
455
|
-
,
|
|
462
|
+
filterable: filterable,
|
|
456
463
|
filter: enableRemoteSearch ? undefined : filterable ? localFilter : undefined,
|
|
457
464
|
onSearch: enableRemoteSearch ? handleSearch : undefined,
|
|
465
|
+
onClear: enableRemoteSearch ? handleClear : undefined,
|
|
458
466
|
loading: loading && options.length === 0,
|
|
459
467
|
multiple: multiple,
|
|
460
468
|
max: max,
|
|
@@ -19,6 +19,11 @@ interface PntGeneralLayoutProps {
|
|
|
19
19
|
* 返回按钮点击回调
|
|
20
20
|
*/
|
|
21
21
|
onBackButtonClick?: (() => void) | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* 是否显示头部底部分割线
|
|
24
|
+
* @default true
|
|
25
|
+
*/
|
|
26
|
+
headerBordered?: boolean;
|
|
22
27
|
/**
|
|
23
28
|
* 子头部内容
|
|
24
29
|
* @description 位于 Header 下方,常用于放置面包屑、Tab 等
|
|
@@ -41,6 +46,25 @@ interface PntGeneralLayoutProps {
|
|
|
41
46
|
* 内容区自定义样式
|
|
42
47
|
*/
|
|
43
48
|
bodyStyle?: React.CSSProperties;
|
|
49
|
+
/**
|
|
50
|
+
* 内容区内边距
|
|
51
|
+
* @description 数字按 px 处理;需要全出血内容时可传 '24px 0'
|
|
52
|
+
* @default 24
|
|
53
|
+
*/
|
|
54
|
+
bodyPadding?: number | string;
|
|
55
|
+
/**
|
|
56
|
+
* 内容区卡片圆角(px)
|
|
57
|
+
* @description 作用于 body 内 t-card / tea-card / 各业务 wrapper、block 容器
|
|
58
|
+
* @default 4
|
|
59
|
+
*/
|
|
60
|
+
cardRadius?: number;
|
|
61
|
+
/**
|
|
62
|
+
* 底部按钮样式模式
|
|
63
|
+
* @description fixed-统一下按钮尺寸(高 36px、最小宽 92px、加粗中号字);
|
|
64
|
+
* auto-不约束按钮尺寸,仅保留按钮间距
|
|
65
|
+
* @default 'fixed'
|
|
66
|
+
*/
|
|
67
|
+
footerButton?: 'fixed' | 'auto';
|
|
44
68
|
/**
|
|
45
69
|
* 底部内容
|
|
46
70
|
* @description 传入后将渲染 Layout Footer 区域,常用于放置操作按钮
|
|
@@ -50,7 +74,8 @@ interface PntGeneralLayoutProps {
|
|
|
50
74
|
/**
|
|
51
75
|
* 通用页面布局组件(L3 布局组件)
|
|
52
76
|
* @description 提供标准的「头部 + 子头部 + 内容区 + 底部」页面布局结构,
|
|
53
|
-
* 基于 TDesign Layout
|
|
77
|
+
* 基于 TDesign Layout(flex column)封装,内容区自动填满剩余高度,
|
|
78
|
+
* 适用于后台管理系统的列表页、详情页、表单页等场景。
|
|
54
79
|
*
|
|
55
80
|
* @example
|
|
56
81
|
* ```tsx
|
|
@@ -76,6 +101,13 @@ interface PntGeneralLayoutProps {
|
|
|
76
101
|
* >
|
|
77
102
|
* <Detail />
|
|
78
103
|
* </PntGeneralLayout>
|
|
104
|
+
*
|
|
105
|
+
* // 全出血内容区(无头部边线、内容无左右内边距、底部按钮不强制尺寸)
|
|
106
|
+
* <PntGeneralLayout
|
|
107
|
+
* headerBordered={false}
|
|
108
|
+
* bodyPadding="24px 0"
|
|
109
|
+
* footerButton="auto"
|
|
110
|
+
* />
|
|
79
111
|
* ```
|
|
80
112
|
*/
|
|
81
113
|
declare const PntGeneralLayout: FunctionComponent<PntGeneralLayoutProps>;
|
|
@@ -7,7 +7,8 @@ import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
7
7
|
/**
|
|
8
8
|
* 通用页面布局组件(L3 布局组件)
|
|
9
9
|
* @description 提供标准的「头部 + 子头部 + 内容区 + 底部」页面布局结构,
|
|
10
|
-
* 基于 TDesign Layout
|
|
10
|
+
* 基于 TDesign Layout(flex column)封装,内容区自动填满剩余高度,
|
|
11
|
+
* 适用于后台管理系统的列表页、详情页、表单页等场景。
|
|
11
12
|
*
|
|
12
13
|
* @example
|
|
13
14
|
* ```tsx
|
|
@@ -33,6 +34,13 @@ import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
33
34
|
* >
|
|
34
35
|
* <Detail />
|
|
35
36
|
* </PntGeneralLayout>
|
|
37
|
+
*
|
|
38
|
+
* // 全出血内容区(无头部边线、内容无左右内边距、底部按钮不强制尺寸)
|
|
39
|
+
* <PntGeneralLayout
|
|
40
|
+
* headerBordered={false}
|
|
41
|
+
* bodyPadding="24px 0"
|
|
42
|
+
* footerButton="auto"
|
|
43
|
+
* />
|
|
36
44
|
* ```
|
|
37
45
|
*/
|
|
38
46
|
const PntGeneralLayout = prop => {
|
|
@@ -41,19 +49,29 @@ const PntGeneralLayout = prop => {
|
|
|
41
49
|
headerTitle,
|
|
42
50
|
showBackButton = false,
|
|
43
51
|
onBackButtonClick,
|
|
52
|
+
headerBordered = true,
|
|
44
53
|
subHeaderContent,
|
|
45
54
|
operation,
|
|
46
55
|
bodyRef,
|
|
47
56
|
children,
|
|
48
57
|
bodyStyle,
|
|
58
|
+
bodyPadding = 24,
|
|
59
|
+
cardRadius = 4,
|
|
60
|
+
footerButton = 'fixed',
|
|
49
61
|
footer
|
|
50
62
|
} = prop;
|
|
51
63
|
const {
|
|
52
64
|
Header,
|
|
53
65
|
Footer
|
|
54
66
|
} = Layout;
|
|
67
|
+
const layoutClassName = ['pnt-general-layout', !headerBordered && 'pnt-general-layout--borderless', footerButton === 'auto' && 'pnt-general-layout--footer-auto', className].filter(Boolean).join(' ');
|
|
68
|
+
const cssVars = {
|
|
69
|
+
'--pnt-layout-body-padding': typeof bodyPadding === 'number' ? `${bodyPadding}px` : bodyPadding,
|
|
70
|
+
'--pnt-layout-card-radius': `${cardRadius}px`
|
|
71
|
+
};
|
|
55
72
|
return /*#__PURE__*/_jsxs(Layout, {
|
|
56
|
-
className:
|
|
73
|
+
className: layoutClassName,
|
|
74
|
+
style: cssVars,
|
|
57
75
|
children: [headerTitle && /*#__PURE__*/_jsxs(Header, {
|
|
58
76
|
children: [/*#__PURE__*/_jsxs("span", {
|
|
59
77
|
className: "header-title",
|
|
@@ -75,10 +93,7 @@ const PntGeneralLayout = prop => {
|
|
|
75
93
|
}), /*#__PURE__*/_jsx("div", {
|
|
76
94
|
className: "general-layout-body",
|
|
77
95
|
ref: bodyRef,
|
|
78
|
-
style:
|
|
79
|
-
height: `calc(100%${headerTitle && ' - 72px'}${footer && ' - 72px'})`,
|
|
80
|
-
...bodyStyle
|
|
81
|
-
},
|
|
96
|
+
style: bodyStyle,
|
|
82
97
|
children: children
|
|
83
98
|
}), footer && /*#__PURE__*/_jsx(Footer, {
|
|
84
99
|
children: footer
|
|
@@ -1,90 +1,121 @@
|
|
|
1
|
-
.pnt-general-layout {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
1
|
+
.pnt-general-layout {
|
|
2
|
+
// 可通过 props(bodyPadding / cardRadius)覆盖的布局变量
|
|
3
|
+
--pnt-layout-body-padding: 24px;
|
|
4
|
+
--pnt-layout-card-radius: 4px;
|
|
5
|
+
|
|
6
|
+
height: 100%;
|
|
7
|
+
background-color: rgba(0, 0, 0, 0);
|
|
8
|
+
|
|
9
|
+
.t-layout__header {
|
|
10
|
+
height: auto;
|
|
11
|
+
flex-shrink: 0;
|
|
12
|
+
display: flex;
|
|
13
|
+
justify-content: space-between;
|
|
14
|
+
align-items: stretch;
|
|
15
|
+
border-bottom: 1px solid #f0f3f6;
|
|
16
|
+
|
|
17
|
+
.header-title {
|
|
18
|
+
display: inline-flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
padding: 24px;
|
|
21
|
+
|
|
22
|
+
.goback-icon {
|
|
23
|
+
margin-right: 12px;
|
|
24
|
+
color: #3f8cff;
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.title {
|
|
29
|
+
font-weight: 600;
|
|
30
|
+
font-size: 20px;
|
|
31
|
+
line-height: 24px;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 统一 flex 居中:高度由 header-title 的 padding 撑起(72px),
|
|
36
|
+
// 比固定 line-height: 72px 更稳,按钮高度变化也不会错位
|
|
37
|
+
.header-operation {
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
padding-right: 24px;
|
|
41
|
+
|
|
42
|
+
.t-button + .t-button {
|
|
43
|
+
margin-left: 12px;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 无头部分割线变体(headerBordered={false})
|
|
49
|
+
&--borderless {
|
|
50
|
+
.t-layout__header {
|
|
51
|
+
border-bottom: none;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.general-layout-sub-header {
|
|
56
|
+
flex-shrink: 0;
|
|
57
|
+
border-bottom: 1px solid #f0f3f6;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.general-layout-body {
|
|
61
|
+
// flex 自动填满 header/sub-header/footer 之外的剩余空间;
|
|
62
|
+
// min-height: 0 保证内容超长时由 body 自身滚动,而不是撑破布局
|
|
63
|
+
flex: 1 1 auto;
|
|
64
|
+
min-height: 0;
|
|
65
|
+
padding: var(--pnt-layout-body-padding);
|
|
66
|
+
overflow-y: auto;
|
|
67
|
+
background: #fafafa;
|
|
68
|
+
|
|
69
|
+
// 卡片/业务容器取各项目选择器超集,统一去阴影、间距与圆角
|
|
70
|
+
> div.tea-card,
|
|
71
|
+
> div.t-card,
|
|
72
|
+
> div.t-loading__parent > div.t-card,
|
|
73
|
+
> div[class*='-wrapper'],
|
|
74
|
+
> div[class*='-block'] {
|
|
75
|
+
box-shadow: none;
|
|
76
|
+
margin-bottom: 24px;
|
|
77
|
+
border-radius: var(--pnt-layout-card-radius);
|
|
78
|
+
|
|
79
|
+
.t-card__body {
|
|
80
|
+
border-radius: var(--pnt-layout-card-radius);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
> div:last-child,
|
|
85
|
+
> div:last-child.t-card,
|
|
86
|
+
> div:last-child.tea-card,
|
|
87
|
+
> div.t-loading__parent > div:last-child.t-card,
|
|
88
|
+
> div:last-child[class*='-wrapper'],
|
|
89
|
+
> div:last-child[class*='-block'] {
|
|
90
|
+
margin-bottom: 0;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.t-layout__footer {
|
|
95
|
+
flex-shrink: 0;
|
|
96
|
+
padding: 18px 24px;
|
|
97
|
+
background: #fff;
|
|
98
|
+
border-top: 1px solid #f0f3f6;
|
|
99
|
+
|
|
100
|
+
.t-button {
|
|
101
|
+
font-size: 14px;
|
|
102
|
+
min-width: 92px;
|
|
103
|
+
height: 36px;
|
|
104
|
+
font-weight: 600;
|
|
105
|
+
|
|
106
|
+
& + .t-button {
|
|
107
|
+
margin-left: 12px;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 底部按钮不约束尺寸变体(footerButton='auto'),仅保留按钮间距
|
|
113
|
+
&--footer-auto {
|
|
114
|
+
.t-layout__footer .t-button {
|
|
115
|
+
font-size: inherit;
|
|
116
|
+
min-width: 0;
|
|
117
|
+
height: auto;
|
|
118
|
+
font-weight: inherit;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
|
+
import './index.scss';
|
|
3
|
+
/**
|
|
4
|
+
* PntImage 文案集合
|
|
5
|
+
* @description 所有内部展示文案均可通过 texts prop 自定义,用于多语言场景
|
|
6
|
+
*/
|
|
7
|
+
export interface PntImageTexts {
|
|
8
|
+
/** 上传中占位文案(仅上传预览模式生效) */
|
|
9
|
+
uploading?: string;
|
|
10
|
+
/** 图片加载中占位文案(纯预览模式生效,透传给 tdesign Image loading) */
|
|
11
|
+
loading?: string;
|
|
12
|
+
/** 图片加载失败占位文案(纯预览模式生效,透传给 tdesign Image error) */
|
|
13
|
+
loadFailed?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface PntImageProps {
|
|
16
|
+
/**
|
|
17
|
+
* 图片地址
|
|
18
|
+
* @description 图片的完整 URL 地址;为空则展示 defaultImg 或内置 uploadImg
|
|
19
|
+
*/
|
|
20
|
+
src: string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* alt 文本
|
|
23
|
+
* @description 图片加载失败时的替代文本,纯预览模式必传以提升可访问性
|
|
24
|
+
*/
|
|
25
|
+
alt?: string;
|
|
26
|
+
/**
|
|
27
|
+
* 默认图片
|
|
28
|
+
* @description src 为空时展示的占位图;不传则使用内置的 uploadImg
|
|
29
|
+
*/
|
|
30
|
+
defaultImg?: string;
|
|
31
|
+
/**
|
|
32
|
+
* 自定义样式
|
|
33
|
+
* @description 一般用于控制图片宽高
|
|
34
|
+
*/
|
|
35
|
+
style?: React.CSSProperties;
|
|
36
|
+
/**
|
|
37
|
+
* 上传中状态
|
|
38
|
+
* @description 当为 true 时显示上传中 loading 占位,阻止其他操作;仅在上传预览模式(传入 onRemove)生效
|
|
39
|
+
*/
|
|
40
|
+
loading?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* 删除回调
|
|
43
|
+
* @description 传入即启用「上传预览模式」,mask 含预览 + 删除按钮;不传则为「纯预览模式」
|
|
44
|
+
* @param e - 事件对象
|
|
45
|
+
*/
|
|
46
|
+
onRemove?: (e: any) => void;
|
|
47
|
+
/**
|
|
48
|
+
* attach 容器
|
|
49
|
+
* @description 弹窗场景下需要透传给 tdesign ImageViewer 的 attach,避免预览浮层被父容器裁剪
|
|
50
|
+
*/
|
|
51
|
+
container?: HTMLElement | (() => HTMLElement);
|
|
52
|
+
/**
|
|
53
|
+
* 多语言文案
|
|
54
|
+
* @description 传入即覆盖默认中文文案;仅对应字段会覆盖,未传字段仍用默认值
|
|
55
|
+
*/
|
|
56
|
+
texts?: PntImageTexts;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* PntImage 业务图片组件
|
|
60
|
+
* @description 统一覆盖「上传图片预览」和「纯图片预览」两种业务场景,避免业务侧维护两套组件
|
|
61
|
+
*
|
|
62
|
+
* 模式判定:传入 `onRemove` 即为上传预览模式,否则为纯预览模式
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```tsx
|
|
66
|
+
* // 上传预览模式
|
|
67
|
+
* <PntImage
|
|
68
|
+
* src={iconUrl}
|
|
69
|
+
* loading={loading}
|
|
70
|
+
* defaultImg={defaultImg}
|
|
71
|
+
* onRemove={handleRemove}
|
|
72
|
+
* style={{ width: 136, height: 136 }}
|
|
73
|
+
* />
|
|
74
|
+
*
|
|
75
|
+
* // 纯预览模式
|
|
76
|
+
* <PntImage
|
|
77
|
+
* src={url}
|
|
78
|
+
* alt="avatar"
|
|
79
|
+
* style={{ width: 60, height: 60 }}
|
|
80
|
+
* container={container}
|
|
81
|
+
* />
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @returns {ReactElement} 业务图片组件
|
|
85
|
+
*/
|
|
86
|
+
declare const PntImage: FC<PntImageProps>;
|
|
87
|
+
export default PntImage;
|