@pnt-team/pnt-component-frontend 0.0.89 → 0.0.92
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 +127 -54
- package/lib/cjs/components/PntBusinessSelect/index.js +13 -32
- package/lib/cjs/components/PntGeneralLayout/index.js +1 -9
- 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 +8 -34
- 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 +2 -2
- 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/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
PNT 前端业务组件库,基于 [dumi2](https://d.umijs.org/) + [father4](https://github.com/umijs/father) 构建,收敛 PNT 业务项目中高价值的重复组件与 Hook,实现单一来源维护。
|
|
4
4
|
|
|
5
|
+
> 当前版本:**0.0.91** · 组件清单:`PntImage` / `PntBusinessSelect` / `PntSecureBox` / `PntGeneralLayout`
|
|
6
|
+
|
|
5
7
|
## 安装
|
|
6
8
|
|
|
7
9
|
```bash
|
|
@@ -12,11 +14,12 @@ yarn add @pnt-team/pnt-component-frontend
|
|
|
12
14
|
|
|
13
15
|
### 依赖要求(peerDependencies)
|
|
14
16
|
|
|
15
|
-
| 依赖
|
|
16
|
-
|
|
|
17
|
-
| `react`
|
|
18
|
-
| `react-dom`
|
|
19
|
-
| `tdesign-react`
|
|
17
|
+
| 依赖 | 版本要求 | 说明 |
|
|
18
|
+
| --------------------- | ---------- | -------------------------------------- |
|
|
19
|
+
| `react` | `>=16.9.0` | React 核心框架 |
|
|
20
|
+
| `react-dom` | `>=16.9.0` | React DOM 渲染 |
|
|
21
|
+
| `tdesign-react` | `>=1.5.0` | UI 组件库(本组件库基于 TDesign 封装) |
|
|
22
|
+
| `tdesign-icons-react` | `>=0.2.0` | TDesign 图标库 |
|
|
20
23
|
|
|
21
24
|
> ⚠️ 请确保项目中已安装以上依赖,否则组件可能无法正常运行。
|
|
22
25
|
|
|
@@ -36,12 +39,15 @@ import http from '你的项目中的 axios 实例';
|
|
|
36
39
|
```tsx
|
|
37
40
|
import {
|
|
38
41
|
// L1 基础展示组件
|
|
39
|
-
|
|
42
|
+
PntImage,
|
|
40
43
|
// L2 业务复合组件
|
|
41
44
|
PntBusinessSelect,
|
|
42
45
|
PntSecureBox,
|
|
43
46
|
// L3 布局组件
|
|
44
47
|
PntGeneralLayout,
|
|
48
|
+
// 配置
|
|
49
|
+
PntProvider,
|
|
50
|
+
usePntConfig,
|
|
45
51
|
} from '@pnt-team/pnt-component-frontend';
|
|
46
52
|
```
|
|
47
53
|
|
|
@@ -49,29 +55,56 @@ import {
|
|
|
49
55
|
|
|
50
56
|
### L1 基础展示组件
|
|
51
57
|
|
|
52
|
-
####
|
|
58
|
+
#### PntImage — 业务图片组件
|
|
53
59
|
|
|
54
|
-
|
|
60
|
+
统一的业务图片组件,单一组件通过 `onRemove` 是否传入自适应「上传预览」与「纯预览」两种模式,避免业务侧维护两套组件(原 UploadImageViewer + BasicImageViewer)。
|
|
55
61
|
|
|
56
62
|
**Props:**
|
|
57
63
|
|
|
58
|
-
| 属性 | 类型
|
|
59
|
-
| ------------ |
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `defaultImg` | `string`
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
64
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
65
|
+
| ------------ | ------------------------------------ | :--: | -------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
66
|
+
| `src` | `string \| undefined` | ✅ | - | 图片 URL;为空则展示 defaultImg 或内置 uploadImg |
|
|
67
|
+
| `alt` | `string` | ❌ | `''` | alt 文本,纯预览模式建议传入以提升可访问性 |
|
|
68
|
+
| `defaultImg` | `string` | ❌ | 内置 uploadImg | 默认图片(仅上传预览模式生效) |
|
|
69
|
+
| `style` | `React.CSSProperties` | ❌ | - | 自定义样式(一般用于控制宽高) |
|
|
70
|
+
| `loading` | `boolean` | ❌ | `false` | 上传中状态(仅上传预览模式生效) |
|
|
71
|
+
| `onRemove` | `(e: any) => void` | ❌ | - | 删除回调(传入即启用上传预览模式) |
|
|
72
|
+
| `container` | `HTMLElement \| (() => HTMLElement)` | ❌ | - | attach 容器(弹窗场景需要,透传给 tdesign ImageViewer) |
|
|
73
|
+
| `texts` | `PntImageTexts` | ❌ | `{ uploading: '上传中', loading: '加载中', loadFailed: '加载失败' }` | 多语言文案,传入字段覆盖默认中文 |
|
|
74
|
+
|
|
75
|
+
**模式判定规则:**
|
|
76
|
+
|
|
77
|
+
| 模式 | 触发条件 | mask 行为 | loading 占位 | defaultImg | error 占位 |
|
|
78
|
+
| ------------ | --------------- | ------------------------ | ----------------------- | :--------: | ---------- |
|
|
79
|
+
| 上传预览模式 | 传入 `onRemove` | 预览按钮 + 分隔线 + 删除 | "上传中"(LoadingIcon) | ✅ | 不显示 |
|
|
80
|
+
| 纯预览模式 | 不传 `onRemove` | 半透明遮罩 + 点击预览 | "加载中"(ImageIcon) | ❌ | "加载失败" |
|
|
65
81
|
|
|
66
82
|
**示例:**
|
|
67
83
|
|
|
68
84
|
```tsx
|
|
69
|
-
|
|
70
|
-
|
|
85
|
+
// 上传预览模式
|
|
86
|
+
<PntImage
|
|
87
|
+
src={iconUrl}
|
|
71
88
|
loading={uploading}
|
|
72
89
|
defaultImg="/path/to/default.png"
|
|
73
|
-
params={{ style: { width: '200px', height: '200px' } }}
|
|
74
90
|
onRemove={handleRemove}
|
|
91
|
+
style={{ width: 136, height: 136 }}
|
|
92
|
+
/>
|
|
93
|
+
|
|
94
|
+
// 纯预览模式
|
|
95
|
+
<PntImage
|
|
96
|
+
src={url}
|
|
97
|
+
alt="avatar"
|
|
98
|
+
style={{ width: 60, height: 60 }}
|
|
99
|
+
container={dialogContainer}
|
|
100
|
+
/>
|
|
101
|
+
|
|
102
|
+
// 多语言
|
|
103
|
+
<PntImage
|
|
104
|
+
src={url}
|
|
105
|
+
alt="avatar"
|
|
106
|
+
style={{ width: 60, height: 60 }}
|
|
107
|
+
texts={{ loading: 'Loading', loadFailed: 'Failed' }}
|
|
75
108
|
/>
|
|
76
109
|
```
|
|
77
110
|
|
|
@@ -81,23 +114,42 @@ import {
|
|
|
81
114
|
|
|
82
115
|
#### PntBusinessSelect — 业务选择器组件
|
|
83
116
|
|
|
84
|
-
统一业务下拉选择器,内置对接后端通用接口 `/api/business/select/list
|
|
117
|
+
统一业务下拉选择器,内置对接后端通用接口 `/api/business/select/list`,支持远程搜索、滚动分页加载、多选、场景化禁用(合规场景 `creatable` 字段)。
|
|
118
|
+
|
|
119
|
+
替代此前 5 个分散的业务 select 接口(`game/list`、`game/list_option`、`compliance-age/game/list`、`biz-list-with-creatable-state`、`sdk_customize/game_list`)。
|
|
85
120
|
|
|
86
121
|
**Props:**
|
|
87
122
|
|
|
88
|
-
| 属性
|
|
89
|
-
|
|
|
90
|
-
| `value`
|
|
91
|
-
| `onChange`
|
|
92
|
-
| `multiple`
|
|
93
|
-
| `scene`
|
|
94
|
-
| `statuses`
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
123
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
124
|
+
| ---------------------- | ----------------------------------------------------------- | :--: | ----------- | ----------------------------------------------------------------------------------- |
|
|
125
|
+
| `value` | `string \| string[]` | ❌ | - | 当前选中值;单选为 string,多选为 string[] |
|
|
126
|
+
| `onChange` | `(value, context) => void` | ❌ | - | 值变化回调;context.selectedOptions 返回纯业务数据对象 |
|
|
127
|
+
| `multiple` | `boolean` | ❌ | `false` | 是否多选 |
|
|
128
|
+
| `scene` | `'default' \| 'sdk' \| 'compliance_age' \| 'custom_region'` | ❌ | `'default'` | 下拉场景;后端按场景返回默认字段集 |
|
|
129
|
+
| `statuses` | `number[]` | ❌ | - | 业务状态筛选(3-线上运营, 4-接入中, 5-中途结项, 6-已下线) |
|
|
130
|
+
| `businessTypes` | `number[]` | ❌ | - | 业务类型筛选(1-正式业务, 2-测试业务) |
|
|
131
|
+
| `businessAccessMethod` | `number` | ❌ | - | 业务接入类型筛选(1-纯 SDK, 2-LI PASS, 3-工作室品牌账号) |
|
|
132
|
+
| `labelFormat` | `'name' \| 'name_en' \| 'name_id' \| 'name_en_id'` | ❌ | `'name'` | 标签格式 |
|
|
133
|
+
| `labelField` | `string` | ❌ | - | 自定义 label 字段(消费方接口返回字段非标准 name/name_en 时使用) |
|
|
134
|
+
| `optionLabel` | `(item) => ReactNode` | ❌ | - | 自定义选项 label 渲染;优先级高于 labelFormat/labelField |
|
|
135
|
+
| `placeholder` | `string` | ❌ | - | 占位提示文案 |
|
|
136
|
+
| `disabled` | `boolean` | ❌ | `false` | 是否禁用整个选择器 |
|
|
137
|
+
| `clearable` | `boolean` | ❌ | `true` | 是否可清除 |
|
|
138
|
+
| `filterable` | `boolean` | ❌ | `true` | 是否可远程搜索 |
|
|
139
|
+
| `max` | `number` | ❌ | - | 多选时最大可选数量 |
|
|
140
|
+
| `pageSize` | `number` | ❌ | `20` | 每页条数 |
|
|
141
|
+
| `searchDebounce` | `number` | ❌ | `400` | 搜索防抖延迟(毫秒) |
|
|
142
|
+
| `minCollapsedNum` | `number` | ❌ | - | 多选时最少折叠数量;透传至 TDesign Select |
|
|
143
|
+
| `collapsedItems` | `TdSelectProps['collapsedItems']` | ❌ | - | 折叠项自定义渲染;透传至 TDesign Select |
|
|
144
|
+
| `popupProps` | `Partial<TdSelectProps['popupProps']>` | ❌ | - | 弹层属性透传;与内部 onScrollToBottom 合并;常用 attach、overlayClassName |
|
|
145
|
+
| `fields` | `string[]` | ❌ | - | 字段白名单,传给后端 fields 参数;未传时后端按 scene 返回默认字段 |
|
|
146
|
+
| `pagination` | `boolean` | ❌ | `true` | 是否分页;false 时一次性加载全量数据,不远程搜索 |
|
|
147
|
+
| `onLoaded` | `(options) => void` | ❌ | - | 选项加载完成回调;不分页模式下首屏即全量,可复用此数据避免重复请求 |
|
|
148
|
+
| `optionDisabled` | `(item) => boolean` | ❌ | - | 自定义选项禁用逻辑;与合规场景 creatable 判断叠加(任一为 true 即禁用) |
|
|
149
|
+
| `optionTooltip` | `(item) => string` | ❌ | - | 自定义选项 tooltip 内容;返回非空字符串时该选项悬停显示 tooltip,常用于解释禁用原因 |
|
|
150
|
+
| `tooltipPlacement` | `'top' \| 'bottom' \| 'left' \| 'right'` | ❌ | `'top'` | tooltip 位置 |
|
|
151
|
+
| `className` | `string` | ❌ | - | 自定义类名 |
|
|
152
|
+
| `style` | `React.CSSProperties` | ❌ | - | 自定义样式 |
|
|
101
153
|
|
|
102
154
|
**示例:**
|
|
103
155
|
|
|
@@ -111,6 +163,15 @@ import { PntBusinessSelect } from '@pnt-team/pnt-component-frontend';
|
|
|
111
163
|
value={gameId}
|
|
112
164
|
onChange={(v) => setGameId(v as string)}
|
|
113
165
|
/>;
|
|
166
|
+
|
|
167
|
+
// 多选 + 状态过滤
|
|
168
|
+
<PntBusinessSelect
|
|
169
|
+
multiple
|
|
170
|
+
statuses={[3, 4]}
|
|
171
|
+
labelFormat="name_id"
|
|
172
|
+
value={gameIds}
|
|
173
|
+
onChange={(v) => setGameIds(v as string[])}
|
|
174
|
+
/>;
|
|
114
175
|
```
|
|
115
176
|
|
|
116
177
|
---
|
|
@@ -121,15 +182,15 @@ import { PntBusinessSelect } from '@pnt-team/pnt-component-frontend';
|
|
|
121
182
|
|
|
122
183
|
**Props:**
|
|
123
184
|
|
|
124
|
-
| 属性 | 类型 | 必填 | 说明
|
|
125
|
-
| ----------- | -------------------------------------- |
|
|
126
|
-
| `dataKey` | `string` |
|
|
127
|
-
| `dataValue` | `string` |
|
|
128
|
-
| `env` | `string \| number` |
|
|
129
|
-
| `gameId` | `string \| number` |
|
|
130
|
-
| `renderDom` | `(content: string) => React.ReactNode` |
|
|
131
|
-
| `viewText` | `string` |
|
|
132
|
-
| `pageId` | `string` |
|
|
185
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
186
|
+
| ----------- | -------------------------------------- | :--: | ------------------- | ------------------------------------------------------------ |
|
|
187
|
+
| `dataKey` | `string` | ✅ | - | 高敏信息的唯一标识 key,用于缓存真实值 |
|
|
188
|
+
| `dataValue` | `string` | ✅ | - | 初始显示值,通常为脱敏后的数据(含 `_high_security_:` 前缀) |
|
|
189
|
+
| `env` | `string \| number` | ✅ | - | 环境标识(如 dev / test / production) |
|
|
190
|
+
| `gameId` | `string \| number` | ✅ | - | 游戏 / 业务 ID |
|
|
191
|
+
| `renderDom` | `(content: string) => React.ReactNode` | ✅ | - | 自定义渲染函数,参数为当前显示内容(真实值或脱敏值) |
|
|
192
|
+
| `viewText` | `string` | ❌ | `'View'` | 查看按钮文案 |
|
|
193
|
+
| `pageId` | `string` | ❌ | `location.pathname` | 页面 ID,用于操作日志和权限验证 |
|
|
133
194
|
|
|
134
195
|
**示例:**
|
|
135
196
|
|
|
@@ -152,22 +213,26 @@ import { PntSecureBox } from '@pnt-team/pnt-component-frontend';
|
|
|
152
213
|
|
|
153
214
|
#### PntGeneralLayout — 通用页面布局组件
|
|
154
215
|
|
|
155
|
-
提供标准的「头部 + 子头部 + 内容区 + 底部」页面布局结构,基于 TDesign Layout
|
|
216
|
+
提供标准的「头部 + 子头部 + 内容区 + 底部」页面布局结构,基于 TDesign Layout(flex column)封装,内容区自动填满剩余高度并独立滚动,适用于后台管理系统的列表页、详情页、表单页等场景。
|
|
156
217
|
|
|
157
218
|
**Props:**
|
|
158
219
|
|
|
159
|
-
| 属性 | 类型 | 必填 | 默认值
|
|
160
|
-
| ------------------- | --------------------- |
|
|
161
|
-
| `className` | `string` |
|
|
162
|
-
| `headerTitle` | `ReactNode` |
|
|
163
|
-
| `showBackButton` | `boolean` |
|
|
164
|
-
| `onBackButtonClick` | `() => void` |
|
|
165
|
-
| `
|
|
166
|
-
| `
|
|
167
|
-
| `
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
220
|
+
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|
|
221
|
+
| ------------------- | --------------------- | :--: | --------- | ---------------------------------------------------------------------------------- |
|
|
222
|
+
| `className` | `string` | ❌ | - | 自定义类名,追加到根元素,常用于页面级样式覆写 |
|
|
223
|
+
| `headerTitle` | `ReactNode` | ❌ | - | 头部标题内容,传入后渲染 Header 区域 |
|
|
224
|
+
| `showBackButton` | `boolean` | ❌ | `false` | 是否显示返回按钮 |
|
|
225
|
+
| `onBackButtonClick` | `() => void` | ❌ | - | 返回按钮点击回调 |
|
|
226
|
+
| `headerBordered` | `boolean` | ❌ | `true` | 是否显示头部底部分割线 |
|
|
227
|
+
| `subHeaderContent` | `ReactNode` | ❌ | - | 子头部内容,位于 Header 下方 |
|
|
228
|
+
| `operation` | `ReactNode` | ❌ | - | 头部右侧操作区,常用于放置按钮组 |
|
|
229
|
+
| `bodyRef` | `Ref<HTMLDivElement>` | ❌ | - | 内容区 ref 引用 |
|
|
230
|
+
| `bodyStyle` | `CSSProperties` | ❌ | - | 内容区自定义样式(内边距、圆角建议优先使用 bodyPadding / cardRadius) |
|
|
231
|
+
| `bodyPadding` | `number \| string` | ❌ | `24` | 内容区内边距;数字按 px 处理,全出血内容可传 `'24px 0'` |
|
|
232
|
+
| `cardRadius` | `number` | ❌ | `4` | 内容区卡片圆角(px),联动 t-card 及其 body |
|
|
233
|
+
| `footerButton` | `'fixed' \| 'auto'` | ❌ | `'fixed'` | fixed:底部按钮统一 36px 高 / 92px 最小宽 / 加粗;auto:不约束按钮尺寸,仅保留间距 |
|
|
234
|
+
| `footer` | `ReactNode` | ❌ | - | 底部内容,传入后渲染 Footer 区域 |
|
|
235
|
+
| `children` | `ReactNode` | ❌ | - | 页面主体内容 |
|
|
171
236
|
|
|
172
237
|
**示例:**
|
|
173
238
|
|
|
@@ -204,6 +269,14 @@ import { PntSecureBox } from '@pnt-team/pnt-component-frontend';
|
|
|
204
269
|
>
|
|
205
270
|
<Detail />
|
|
206
271
|
</PntGeneralLayout>
|
|
272
|
+
|
|
273
|
+
// 适配项目风格(无头部边线 / 全出血内容 / 大圆角卡片 / 底部按钮自适应)
|
|
274
|
+
<PntGeneralLayout
|
|
275
|
+
headerBordered={false}
|
|
276
|
+
bodyPadding="24px 0"
|
|
277
|
+
cardRadius={8}
|
|
278
|
+
footerButton="auto"
|
|
279
|
+
/>
|
|
207
280
|
```
|
|
208
281
|
|
|
209
282
|
---
|
|
@@ -110,60 +110,46 @@ var PntBusinessSelect = (props) => {
|
|
|
110
110
|
);
|
|
111
111
|
const fetchData = (0, import_react.useCallback)(
|
|
112
112
|
async (kw, page, append) => {
|
|
113
|
-
var _a, _b
|
|
113
|
+
var _a, _b;
|
|
114
114
|
if (append) {
|
|
115
|
-
if (appendLoadingRef.current || loadingRef.current)
|
|
116
|
-
console.log("[PntBS] fetchData APPEND blocked: loading=" + loadingRef.current + " appendLoading=" + appendLoadingRef.current);
|
|
115
|
+
if (appendLoadingRef.current || loadingRef.current)
|
|
117
116
|
return;
|
|
118
|
-
}
|
|
119
117
|
const meta = listMetaRef.current;
|
|
120
|
-
if (meta.keyword !== kw)
|
|
121
|
-
console.log("[PntBS] fetchData APPEND blocked: kw mismatch " + kw + " vs " + meta.keyword);
|
|
118
|
+
if (meta.keyword !== kw)
|
|
122
119
|
return;
|
|
123
|
-
|
|
124
|
-
if (page <= meta.page) {
|
|
125
|
-
console.log("[PntBS] fetchData APPEND blocked: page " + page + " <= " + meta.page);
|
|
120
|
+
if (page <= meta.page)
|
|
126
121
|
return;
|
|
127
|
-
|
|
128
|
-
if (optionsRef.current.length >= meta.total) {
|
|
129
|
-
console.log("[PntBS] fetchData APPEND blocked: no more");
|
|
122
|
+
if (optionsRef.current.length >= meta.total)
|
|
130
123
|
return;
|
|
131
|
-
}
|
|
132
124
|
appendLoadingRef.current = true;
|
|
133
125
|
}
|
|
134
126
|
const seq = ++fetchSeqRef.current;
|
|
135
|
-
console.log('[PntBS] fetchData START kw="' + kw + '" page=' + page + " append=" + append + " seq=" + seq);
|
|
136
127
|
loadingRef.current = true;
|
|
137
128
|
setLoading(true);
|
|
138
129
|
try {
|
|
139
130
|
const res = await requestFn(buildParams(kw, page));
|
|
140
|
-
|
|
141
|
-
if (seq !== fetchSeqRef.current) {
|
|
142
|
-
console.log("[PntBS] fetchData seq MISMATCH - DROPPED (seq=" + seq + " latest=" + fetchSeqRef.current + ")");
|
|
131
|
+
if (seq !== fetchSeqRef.current)
|
|
143
132
|
return;
|
|
144
|
-
}
|
|
145
133
|
if (res.code === 0 && res.data) {
|
|
146
134
|
const newList = res.data.list || [];
|
|
147
135
|
const resTotal = ((_a = res.data.pagination) == null ? void 0 : _a.total) || 0;
|
|
148
136
|
let nextList;
|
|
149
137
|
if (append) {
|
|
150
138
|
const existed = new Set(optionsRef.current.map((o) => o.game_id));
|
|
151
|
-
nextList = [
|
|
139
|
+
nextList = [
|
|
140
|
+
...optionsRef.current,
|
|
141
|
+
...newList.filter((o) => !existed.has(o.game_id))
|
|
142
|
+
];
|
|
152
143
|
} else {
|
|
153
144
|
nextList = newList;
|
|
154
145
|
lastReplaceAtRef.current = Date.now();
|
|
155
146
|
}
|
|
156
147
|
optionsRef.current = nextList;
|
|
157
148
|
listMetaRef.current = { keyword: kw, page, total: resTotal };
|
|
158
|
-
console.log("[PntBS] fetchData SET options count=" + nextList.length + " first=" + (((_b = nextList[0]) == null ? void 0 : _b.name) || "null"));
|
|
159
149
|
setOptions(nextList);
|
|
160
|
-
(
|
|
161
|
-
} else {
|
|
162
|
-
console.log("[PntBS] fetchData BAD RESPONSE code=" + res.code + " hasData=" + !!res.data);
|
|
150
|
+
(_b = onLoadedRef.current) == null ? void 0 : _b.call(onLoadedRef, nextList);
|
|
163
151
|
}
|
|
164
|
-
} catch
|
|
165
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
166
|
-
console.log("[PntBS] fetchData ERROR seq=" + seq + ' kw="' + kw + '": ' + msg);
|
|
152
|
+
} catch {
|
|
167
153
|
} finally {
|
|
168
154
|
if (append)
|
|
169
155
|
appendLoadingRef.current = false;
|
|
@@ -178,13 +164,10 @@ var PntBusinessSelect = (props) => {
|
|
|
178
164
|
const handleSearch = (0, import_react.useCallback)(
|
|
179
165
|
(val) => {
|
|
180
166
|
keywordRef.current = val;
|
|
181
|
-
console.log('[PntBS] handleSearch val="' + val + '" debounceMs=' + searchDebounce);
|
|
182
167
|
if (debounceRef.current)
|
|
183
168
|
clearTimeout(debounceRef.current);
|
|
184
169
|
debounceRef.current = setTimeout(() => {
|
|
185
|
-
|
|
186
|
-
console.log('[PntBS] handleSearch timer fired, fetchData kw="' + kw + '" append=false seq++');
|
|
187
|
-
fetchData(kw, 1, false);
|
|
170
|
+
fetchData(keywordRef.current, 1, false);
|
|
188
171
|
}, searchDebounce);
|
|
189
172
|
},
|
|
190
173
|
[fetchData, searchDebounce]
|
|
@@ -203,7 +186,6 @@ var PntBusinessSelect = (props) => {
|
|
|
203
186
|
clearTimeout(debounceRef.current);
|
|
204
187
|
keywordRef.current = "";
|
|
205
188
|
if (listMetaRef.current.keyword) {
|
|
206
|
-
console.log('[PntBS] popupClose resetting from kw="' + listMetaRef.current.keyword + '"');
|
|
207
189
|
fetchData("", 1, false);
|
|
208
190
|
}
|
|
209
191
|
},
|
|
@@ -400,7 +382,6 @@ var PntBusinessSelect = (props) => {
|
|
|
400
382
|
[onChange, multiple, getLabelText]
|
|
401
383
|
);
|
|
402
384
|
const handleClear = (0, import_react.useCallback)(() => {
|
|
403
|
-
console.log("[PntBS] handleClear X-button clicked, resetting");
|
|
404
385
|
keywordRef.current = "";
|
|
405
386
|
if (debounceRef.current)
|
|
406
387
|
clearTimeout(debounceRef.current);
|
|
@@ -70,15 +70,7 @@ var PntGeneralLayout = (prop) => {
|
|
|
70
70
|
operation && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "header-operation", children: operation })
|
|
71
71
|
] }),
|
|
72
72
|
subHeaderContent && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "general-layout-sub-header", children: subHeaderContent }),
|
|
73
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
74
|
-
"div",
|
|
75
|
-
{
|
|
76
|
-
className: "general-layout-body",
|
|
77
|
-
ref: bodyRef,
|
|
78
|
-
style: bodyStyle,
|
|
79
|
-
children
|
|
80
|
-
}
|
|
81
|
-
),
|
|
73
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "general-layout-body", ref: bodyRef, style: bodyStyle, children }),
|
|
82
74
|
footer && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Footer, { children: footer })
|
|
83
75
|
] });
|
|
84
76
|
};
|
|
@@ -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;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/components/PntImage/index.tsx
|
|
30
|
+
var PntImage_exports = {};
|
|
31
|
+
__export(PntImage_exports, {
|
|
32
|
+
default: () => PntImage_default
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(PntImage_exports);
|
|
35
|
+
var import_uploadImg = __toESM(require("../../assets/images/uploadImg.png"));
|
|
36
|
+
var import_tdesign_icons_react = require("tdesign-icons-react");
|
|
37
|
+
var import_tdesign_react = require("tdesign-react");
|
|
38
|
+
var import_index = require("./index.scss");
|
|
39
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
40
|
+
var DEFAULT_TEXTS = {
|
|
41
|
+
uploading: "上传中",
|
|
42
|
+
loading: "加载中",
|
|
43
|
+
loadFailed: "加载失败"
|
|
44
|
+
};
|
|
45
|
+
var PntImage = ({
|
|
46
|
+
src,
|
|
47
|
+
alt = "",
|
|
48
|
+
defaultImg,
|
|
49
|
+
style,
|
|
50
|
+
loading = false,
|
|
51
|
+
onRemove,
|
|
52
|
+
container,
|
|
53
|
+
texts
|
|
54
|
+
}) => {
|
|
55
|
+
const isUploadMode = typeof onRemove === "function";
|
|
56
|
+
const finalSrc = (src == null ? void 0 : src.length) ? src : defaultImg || import_uploadImg.default;
|
|
57
|
+
const attach = container ? typeof container === "function" ? container : () => container : void 0;
|
|
58
|
+
const finalTexts = { ...DEFAULT_TEXTS, ...texts };
|
|
59
|
+
const UploadLoadingContent = (isImageLoading) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
60
|
+
"div",
|
|
61
|
+
{
|
|
62
|
+
className: isImageLoading ? "pnt-image-progress" : "pnt-image-upload-progress",
|
|
63
|
+
style,
|
|
64
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-image-loading", children: [
|
|
65
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tdesign_icons_react.LoadingIcon, { size: "18px", name: "loading" }),
|
|
66
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: finalTexts.uploading })
|
|
67
|
+
] })
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
const basicImageLoading = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
71
|
+
import_tdesign_react.Space,
|
|
72
|
+
{
|
|
73
|
+
direction: "vertical",
|
|
74
|
+
align: "center",
|
|
75
|
+
size: "small",
|
|
76
|
+
style: { color: "var(--td-text-color-disabled)" },
|
|
77
|
+
children: [
|
|
78
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tdesign_icons_react.ImageIcon, { size: 24 }),
|
|
79
|
+
finalTexts.loading
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
const basicImageError = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_tdesign_react.Space, { direction: "vertical", align: "center", size: "small", children: [
|
|
84
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tdesign_icons_react.ImageErrorIcon, { size: 24 }),
|
|
85
|
+
finalTexts.loadFailed
|
|
86
|
+
] });
|
|
87
|
+
const basicMask = (open) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
88
|
+
"div",
|
|
89
|
+
{
|
|
90
|
+
className: "pnt-image-mask pnt-image-mask--basic",
|
|
91
|
+
style: { background: "rgba(0,0,0,.1)", height: "100%" },
|
|
92
|
+
onClick: open
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
const uploadMask = (open) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-image-mask pnt-image-mask--upload", children: [
|
|
96
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tdesign_icons_react.BrowseIcon, { onClick: open, size: "16px", name: "browse" }) }),
|
|
97
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pnt-image-divider" }),
|
|
98
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tdesign_icons_react.DeleteIcon, { onClick: onRemove, size: "16px", name: "delete" }) })
|
|
99
|
+
] });
|
|
100
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
101
|
+
"div",
|
|
102
|
+
{
|
|
103
|
+
className: "pnt-image-wrapper",
|
|
104
|
+
onClick: (e) => {
|
|
105
|
+
if (isUploadMode && ((src == null ? void 0 : src.length) || loading)) {
|
|
106
|
+
e.stopPropagation();
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
children: isUploadMode && loading ? UploadLoadingContent(false) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
110
|
+
import_tdesign_react.ImageViewer,
|
|
111
|
+
{
|
|
112
|
+
images: [finalSrc],
|
|
113
|
+
closeOnOverlay: true,
|
|
114
|
+
attach,
|
|
115
|
+
trigger: ({ open }) => {
|
|
116
|
+
const mask = isUploadMode ? uploadMask(open) : basicMask(open);
|
|
117
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
118
|
+
import_tdesign_react.Image,
|
|
119
|
+
{
|
|
120
|
+
alt,
|
|
121
|
+
src: finalSrc,
|
|
122
|
+
loading: isUploadMode ? UploadLoadingContent(true) : basicImageLoading,
|
|
123
|
+
error: isUploadMode ? void 0 : basicImageError,
|
|
124
|
+
overlayContent: isUploadMode ? src ? mask : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, {}) : mask,
|
|
125
|
+
overlayTrigger: "hover",
|
|
126
|
+
fit: "contain",
|
|
127
|
+
className: "pnt-image",
|
|
128
|
+
style
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
var PntImage_default = PntImage;
|