@pnt-team/pnt-component-frontend 0.0.91 → 0.0.95

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.
Files changed (32) hide show
  1. package/README.md +149 -54
  2. package/lib/cjs/api/cos.d.ts +103 -0
  3. package/lib/cjs/api/cos.js +137 -0
  4. package/lib/cjs/components/PntCosUploader/index.d.ts +135 -0
  5. package/lib/cjs/components/PntCosUploader/index.js +209 -0
  6. package/lib/cjs/components/PntImage/index.d.ts +9 -3
  7. package/lib/cjs/components/PntImage/index.js +9 -1
  8. package/lib/cjs/components/PntSecureBox/index.js +8 -5
  9. package/lib/cjs/components/PntUpload/index.d.ts +149 -0
  10. package/lib/cjs/components/PntUpload/index.js +228 -0
  11. package/lib/cjs/components/PntUpload/index.scss +101 -0
  12. package/lib/cjs/hooks/useHighSecurity.d.ts +55 -15
  13. package/lib/cjs/hooks/useHighSecurity.js +168 -35
  14. package/lib/cjs/index.d.ts +6 -0
  15. package/lib/cjs/index.js +18 -2
  16. package/lib/esm/api/cos.d.ts +103 -0
  17. package/lib/esm/api/cos.js +189 -0
  18. package/lib/esm/components/PntCosUploader/index.d.ts +135 -0
  19. package/lib/esm/components/PntCosUploader/index.js +289 -0
  20. package/lib/esm/components/PntImage/index.d.ts +9 -3
  21. package/lib/esm/components/PntImage/index.js +22 -3
  22. package/lib/esm/components/PntSecureBox/index.js +9 -3
  23. package/lib/esm/components/PntUpload/index.d.ts +149 -0
  24. package/lib/esm/components/PntUpload/index.js +276 -0
  25. package/lib/esm/components/PntUpload/index.scss +101 -0
  26. package/lib/esm/hooks/useHighSecurity.d.ts +55 -15
  27. package/lib/esm/hooks/useHighSecurity.js +174 -32
  28. package/lib/esm/index.d.ts +6 -0
  29. package/lib/esm/index.js +9 -1
  30. package/lib/umd/pnt-component-frontend.min.css +1 -1
  31. package/lib/umd/pnt-component-frontend.min.js +1 -1
  32. package/package.json +2 -2
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` | `>=16.9.0` | React 核心框架 |
18
- | `react-dom` | `>=16.9.0` | React DOM 渲染 |
19
- | `tdesign-react` | `>=1.5.0` | UI 组件库(本组件库基于 TDesign 封装) |
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
- PntImagePreview,
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,57 @@ import {
49
55
 
50
56
  ### L1 基础展示组件
51
57
 
52
- #### PntImagePreview — 图片预览组件
58
+ #### PntImage — 业务图片组件
53
59
 
54
- 用于展示上传的图片,支持预览、删除功能和上传状态显示。
60
+ 统一的业务图片组件,单一组件通过 `onRemove` 是否传入自适应「上传预览」与「纯预览」两种模式,避免业务侧维护两套组件(原 UploadImageViewer + BasicImageViewer)。
55
61
 
56
62
  **Props:**
57
63
 
58
- | 属性 | 类型 | 必填 | 说明 |
59
- | ------------ | ----------------------------------------------- | ---- | -------------------------------- |
60
- | `iconUrl` | `string \| undefined` | ✅ | 图片 URL 地址 |
61
- | `loading` | `boolean` | ✅ | 上传加载状态 |
62
- | `defaultImg` | `string` | ❌ | 默认图片(不传则使用内置默认图) |
63
- | `params` | `{ style?: CSSProperties; [key: string]: any }` | ❌ | 自定义扩展参数 |
64
- | `onRemove` | `(e: any) => void` | ❌ | 删除回调函数 |
64
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
65
+ | ------------ | ------------------------------------ | :--: | -------------------------------------------------------------------- | ------------------------------------------------------- |
66
+ | `src` | `string \| undefined` | ✅ | - | 图片 URL;为空则展示 defaultImg 或内置 uploadImg |
67
+ | `alt` | `string` | ❌ | `''` | alt 文本,纯预览模式建议传入以提升可访问性 |
68
+ | `defaultImg` | `string` | ❌ | 内置 uploadImg | 默认图片(仅上传预览模式生效) |
69
+ | `size` | `'small' \| 'medium' \| 'large'` | ❌ | `'large'` | 快捷正方形尺寸:small=36px、medium=60px、large=136px |
70
+ | `style` | `React.CSSProperties` | ❌ | - | 自定义样式;width/height 优先级高于 size |
71
+ | `loading` | `boolean` | ❌ | `false` | 上传中状态(仅上传预览模式生效) |
72
+ | `onRemove` | `(e: any) => void` | ❌ | - | 删除回调(传入即启用上传预览模式) |
73
+ | `container` | `HTMLElement \| (() => HTMLElement)` | ❌ | - | attach 容器(弹窗场景需要,透传给 tdesign ImageViewer) |
74
+ | `texts` | `PntImageTexts` | ❌ | `{ uploading: '上传中', loading: '加载中', loadFailed: '加载失败' }` | 多语言文案,传入字段覆盖默认中文 |
75
+
76
+ **模式判定规则:**
77
+
78
+ | 模式 | 触发条件 | mask 行为 | loading 占位 | defaultImg | error 占位 |
79
+ | ------------ | --------------- | ------------------------ | ----------------------- | :--------: | ---------- |
80
+ | 上传预览模式 | 传入 `onRemove` | 预览按钮 + 分隔线 + 删除 | "上传中"(LoadingIcon) | ✅ | 不显示 |
81
+ | 纯预览模式 | 不传 `onRemove` | 半透明遮罩 + 点击预览 | "加载中"(ImageIcon) | ❌ | "加载失败" |
65
82
 
66
83
  **示例:**
67
84
 
68
85
  ```tsx
69
- <PntImagePreview
70
- iconUrl={imageUrl}
86
+ // 上传预览模式
87
+ <PntImage
88
+ src={iconUrl}
71
89
  loading={uploading}
72
90
  defaultImg="/path/to/default.png"
73
- params={{ style: { width: '200px', height: '200px' } }}
74
91
  onRemove={handleRemove}
92
+ size="large"
93
+ />
94
+
95
+ // 纯预览模式
96
+ <PntImage
97
+ src={url}
98
+ alt="avatar"
99
+ size="medium"
100
+ container={dialogContainer}
101
+ />
102
+
103
+ // 多语言
104
+ <PntImage
105
+ src={url}
106
+ alt="avatar"
107
+ size="medium"
108
+ texts={{ loading: 'Loading', loadFailed: 'Failed' }}
75
109
  />
76
110
  ```
77
111
 
@@ -81,23 +115,42 @@ import {
81
115
 
82
116
  #### PntBusinessSelect — 业务选择器组件
83
117
 
84
- 统一业务下拉选择器,内置对接后端通用接口 `/api/business/select/list`,支持远程搜索、滚动分页加载、多选、场景化禁用。
118
+ 统一业务下拉选择器,内置对接后端通用接口 `/api/business/select/list`,支持远程搜索、滚动分页加载、多选、场景化禁用(合规场景 `creatable` 字段)。
119
+
120
+ 替代此前 5 个分散的业务 select 接口(`game/list`、`game/list_option`、`compliance-age/game/list`、`biz-list-with-creatable-state`、`sdk_customize/game_list`)。
85
121
 
86
122
  **Props:**
87
123
 
88
- | 属性 | 类型 | 必填 | 默认值 | 说明 |
89
- | ------------- | ----------------------------------------------------------- | :--: | ----------- | -------------- |
90
- | `value` | `SelectValue` | ❌ | - | 当前选中值 |
91
- | `onChange` | `TdSelectProps['onChange']` | ❌ | - | 值变化回调 |
92
- | `multiple` | `boolean` | ❌ | `false` | 是否多选 |
93
- | `scene` | `'default' \| 'sdk' \| 'compliance_age' \| 'custom_region'` | ❌ | `'default'` | 下拉场景 |
94
- | `statuses` | `number[]` | ❌ | - | 业务状态筛选 |
95
- | `labelFormat` | `'name' \| 'name_en' \| 'name_id' \| 'name_en_id'` | ❌ | `'name'` | 标签格式 |
96
- | `filterable` | `boolean` | ❌ | `true` | 是否可远程搜索 |
97
- | `pagination` | `boolean` | ❌ | `true` | 是否分页 |
98
- | `placeholder` | `string` | ❌ | - | 占位提示文案 |
99
- | `disabled` | `boolean` | ❌ | `false` | 是否禁用 |
100
- | `popupProps` | `Partial<TdSelectProps['popupProps']>` | ❌ | - | 弹层属性透传 |
124
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
125
+ | ---------------------- | ----------------------------------------------------------- | :--: | ----------- | ----------------------------------------------------------------------------------- |
126
+ | `value` | `string \| string[]` | ❌ | - | 当前选中值;单选为 string,多选为 string[] |
127
+ | `onChange` | `(value, context) => void` | ❌ | - | 值变化回调;context.selectedOptions 返回纯业务数据对象 |
128
+ | `multiple` | `boolean` | ❌ | `false` | 是否多选 |
129
+ | `scene` | `'default' \| 'sdk' \| 'compliance_age' \| 'custom_region'` | ❌ | `'default'` | 下拉场景;后端按场景返回默认字段集 |
130
+ | `statuses` | `number[]` | ❌ | - | 业务状态筛选(3-线上运营, 4-接入中, 5-中途结项, 6-已下线) |
131
+ | `businessTypes` | `number[]` | ❌ | - | 业务类型筛选(1-正式业务, 2-测试业务) |
132
+ | `businessAccessMethod` | `number` | ❌ | - | 业务接入类型筛选(1-纯 SDK, 2-LI PASS, 3-工作室品牌账号) |
133
+ | `labelFormat` | `'name' \| 'name_en' \| 'name_id' \| 'name_en_id'` | ❌ | `'name'` | 标签格式 |
134
+ | `labelField` | `string` | ❌ | - | 自定义 label 字段(消费方接口返回字段非标准 name/name_en 时使用) |
135
+ | `optionLabel` | `(item) => ReactNode` | ❌ | - | 自定义选项 label 渲染;优先级高于 labelFormat/labelField |
136
+ | `placeholder` | `string` | ❌ | - | 占位提示文案 |
137
+ | `disabled` | `boolean` | ❌ | `false` | 是否禁用整个选择器 |
138
+ | `clearable` | `boolean` | ❌ | `true` | 是否可清除 |
139
+ | `filterable` | `boolean` | ❌ | `true` | 是否可远程搜索 |
140
+ | `max` | `number` | ❌ | - | 多选时最大可选数量 |
141
+ | `pageSize` | `number` | ❌ | `20` | 每页条数 |
142
+ | `searchDebounce` | `number` | ❌ | `400` | 搜索防抖延迟(毫秒) |
143
+ | `minCollapsedNum` | `number` | ❌ | - | 多选时最少折叠数量;透传至 TDesign Select |
144
+ | `collapsedItems` | `TdSelectProps['collapsedItems']` | ❌ | - | 折叠项自定义渲染;透传至 TDesign Select |
145
+ | `popupProps` | `Partial<TdSelectProps['popupProps']>` | ❌ | - | 弹层属性透传;与内部 onScrollToBottom 合并;常用 attach、overlayClassName |
146
+ | `fields` | `string[]` | ❌ | - | 字段白名单,传给后端 fields 参数;未传时后端按 scene 返回默认字段 |
147
+ | `pagination` | `boolean` | ❌ | `true` | 是否分页;false 时一次性加载全量数据,不远程搜索 |
148
+ | `onLoaded` | `(options) => void` | ❌ | - | 选项加载完成回调;不分页模式下首屏即全量,可复用此数据避免重复请求 |
149
+ | `optionDisabled` | `(item) => boolean` | ❌ | - | 自定义选项禁用逻辑;与合规场景 creatable 判断叠加(任一为 true 即禁用) |
150
+ | `optionTooltip` | `(item) => string` | ❌ | - | 自定义选项 tooltip 内容;返回非空字符串时该选项悬停显示 tooltip,常用于解释禁用原因 |
151
+ | `tooltipPlacement` | `'top' \| 'bottom' \| 'left' \| 'right'` | ❌ | `'top'` | tooltip 位置 |
152
+ | `className` | `string` | ❌ | - | 自定义类名 |
153
+ | `style` | `React.CSSProperties` | ❌ | - | 自定义样式 |
101
154
 
102
155
  **示例:**
103
156
 
@@ -111,6 +164,15 @@ import { PntBusinessSelect } from '@pnt-team/pnt-component-frontend';
111
164
  value={gameId}
112
165
  onChange={(v) => setGameId(v as string)}
113
166
  />;
167
+
168
+ // 多选 + 状态过滤
169
+ <PntBusinessSelect
170
+ multiple
171
+ statuses={[3, 4]}
172
+ labelFormat="name_id"
173
+ value={gameIds}
174
+ onChange={(v) => setGameIds(v as string[])}
175
+ />;
114
176
  ```
115
177
 
116
178
  ---
@@ -121,15 +183,15 @@ import { PntBusinessSelect } from '@pnt-team/pnt-component-frontend';
121
183
 
122
184
  **Props:**
123
185
 
124
- | 属性 | 类型 | 必填 | 说明 |
125
- | ----------- | -------------------------------------- | ---- | -------------------------------------- |
126
- | `dataKey` | `string` | ✅ | 高敏信息的唯一标识 key |
127
- | `dataValue` | `string` | ✅ | 初始显示值(通常为脱敏后的数据) |
128
- | `env` | `string \| number` | ✅ | 环境标识(如 dev / test / production) |
129
- | `gameId` | `string \| number` | ✅ | 游戏 ID |
130
- | `renderDom` | `(content: string) => React.ReactNode` | ✅ | 自定义渲染函数 |
131
- | `viewText` | `string` | ❌ | 查看按钮文案,默认 `'View'` |
132
- | `pageId` | `string` | ❌ | 页面 ID(用于操作日志和权限验证) |
186
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
187
+ | ----------- | -------------------------------------- | :--: | ------------------- | ------------------------------------------------------------ |
188
+ | `dataKey` | `string` | ✅ | - | 高敏信息的唯一标识 key,用于缓存真实值 |
189
+ | `dataValue` | `string` | ✅ | - | 初始显示值,通常为脱敏后的数据(含 `_high_security_:` 前缀) |
190
+ | `env` | `string \| number` | ✅ | - | 环境标识(如 dev / test / production) |
191
+ | `gameId` | `string \| number` | ✅ | - | 游戏 / 业务 ID |
192
+ | `renderDom` | `(content: string) => React.ReactNode` | ✅ | - | 自定义渲染函数,参数为当前显示内容(真实值或脱敏值) |
193
+ | `viewText` | `string` | ❌ | `'View'` | 查看按钮文案 |
194
+ | `pageId` | `string` | ❌ | `location.pathname` | 页面 ID,用于操作日志和权限验证 |
133
195
 
134
196
  **示例:**
135
197
 
@@ -146,28 +208,53 @@ import { PntSecureBox } from '@pnt-team/pnt-component-frontend';
146
208
  />;
147
209
  ```
148
210
 
211
+ **非展示场景(查看 → 复制 / 批量解锁拼装)请用 `useHighSecurity`:**
212
+
213
+ ```tsx
214
+ import { useHighSecurity } from '@pnt-team/pnt-component-frontend';
215
+
216
+ const { securityData, isUnlocked, requestSecurityData, clearSecurityData } =
217
+ useHighSecurity({ gameId, env });
218
+
219
+ // 第一次点击解锁、第二次点击复制;切换业务时 clearSecurityData()
220
+ const res = await requestSecurityData({
221
+ gameId,
222
+ env,
223
+ param: [{ key: 'SDK_KEY', value: cipher }],
224
+ });
225
+ if (res.length) navigator.clipboard.writeText(securityData.SDK_KEY);
226
+ ```
227
+
228
+ - 模块级缓存按 `gameId + env + key` 复合键隔离,同页多实例共享,同一 key 同页只请求一次;在途并发自动去重
229
+ - 解锁失败返回 `[]` 且不抛错;`env` 必填
230
+ - 另有 `isSecurityData(value)` 导出,用于判断 `_high_security_:` 密文
231
+
149
232
  ---
150
233
 
151
234
  ### L3 布局组件
152
235
 
153
236
  #### PntGeneralLayout — 通用页面布局组件
154
237
 
155
- 提供标准的「头部 + 子头部 + 内容区 + 底部」页面布局结构,基于 TDesign Layout 封装,适用于后台管理系统的详情页、表单页等场景。
238
+ 提供标准的「头部 + 子头部 + 内容区 + 底部」页面布局结构,基于 TDesign Layout(flex column)封装,内容区自动填满剩余高度并独立滚动,适用于后台管理系统的列表页、详情页、表单页等场景。
156
239
 
157
240
  **Props:**
158
241
 
159
- | 属性 | 类型 | 必填 | 默认值 | 说明 |
160
- | ------------------- | --------------------- | ---- | ------ | ------------------------------------ |
161
- | `className` | `string` | ❌ | - | 自定义类名 |
162
- | `headerTitle` | `ReactNode` | ❌ | - | 头部标题内容,传入后渲染 Header 区域 |
163
- | `showBackButton` | `boolean` | ❌ | false | 是否显示返回按钮 |
164
- | `onBackButtonClick` | `() => void` | ❌ | - | 返回按钮点击回调 |
165
- | `subHeaderContent` | `ReactNode` | ❌ | - | 子头部内容,位于 Header 下方 |
166
- | `operation` | `ReactNode` | ❌ | - | 头部右侧操作区,常用于放置按钮组 |
167
- | `bodyRef` | `Ref<HTMLDivElement>` | ❌ | - | 内容区 ref 引用 |
168
- | `bodyStyle` | `CSSProperties` | ❌ | - | 内容区自定义样式 |
169
- | `footer` | `ReactNode` | ❌ | - | 底部内容,传入后渲染 Footer 区域 |
170
- | `children` | `ReactNode` | ❌ | - | 页面主体内容 |
242
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
243
+ | ------------------- | --------------------- | :--: | --------- | ---------------------------------------------------------------------------------- |
244
+ | `className` | `string` | ❌ | - | 自定义类名,追加到根元素,常用于页面级样式覆写 |
245
+ | `headerTitle` | `ReactNode` | ❌ | - | 头部标题内容,传入后渲染 Header 区域 |
246
+ | `showBackButton` | `boolean` | ❌ | `false` | 是否显示返回按钮 |
247
+ | `onBackButtonClick` | `() => void` | ❌ | - | 返回按钮点击回调 |
248
+ | `headerBordered` | `boolean` | ❌ | `true` | 是否显示头部底部分割线 |
249
+ | `subHeaderContent` | `ReactNode` | ❌ | - | 子头部内容,位于 Header 下方 |
250
+ | `operation` | `ReactNode` | ❌ | - | 头部右侧操作区,常用于放置按钮组 |
251
+ | `bodyRef` | `Ref<HTMLDivElement>` | ❌ | - | 内容区 ref 引用 |
252
+ | `bodyStyle` | `CSSProperties` | ❌ | - | 内容区自定义样式(内边距、圆角建议优先使用 bodyPadding / cardRadius) |
253
+ | `bodyPadding` | `number \| string` | ❌ | `24` | 内容区内边距;数字按 px 处理,全出血内容可传 `'24px 0'` |
254
+ | `cardRadius` | `number` | ❌ | `4` | 内容区卡片圆角(px),联动 t-card 及其 body |
255
+ | `footerButton` | `'fixed' \| 'auto'` | ❌ | `'fixed'` | fixed:底部按钮统一 36px 高 / 92px 最小宽 / 加粗;auto:不约束按钮尺寸,仅保留间距 |
256
+ | `footer` | `ReactNode` | ❌ | - | 底部内容,传入后渲染 Footer 区域 |
257
+ | `children` | `ReactNode` | ❌ | - | 页面主体内容 |
171
258
 
172
259
  **示例:**
173
260
 
@@ -204,6 +291,14 @@ import { PntSecureBox } from '@pnt-team/pnt-component-frontend';
204
291
  >
205
292
  <Detail />
206
293
  </PntGeneralLayout>
294
+
295
+ // 适配项目风格(无头部边线 / 全出血内容 / 大圆角卡片 / 底部按钮自适应)
296
+ <PntGeneralLayout
297
+ headerBordered={false}
298
+ bodyPadding="24px 0"
299
+ cardRadius={8}
300
+ footerButton="auto"
301
+ />
207
302
  ```
208
303
 
209
304
  ---
@@ -0,0 +1,103 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ /**
3
+ * 归一化后的 COS 临时凭证
4
+ * @description 各后端接口字段命名不统一(snake/camel),组件库内部统一为 camelCase
5
+ */
6
+ export interface NormalizedCosCredential {
7
+ /** COS 存储桶 */
8
+ bucket: string;
9
+ /** COS 地域 */
10
+ region: string;
11
+ /** 临时密钥 ID */
12
+ secretId: string;
13
+ /** 临时密钥 Key */
14
+ secretKey: string;
15
+ /** 临时会话令牌 */
16
+ sessionToken: string;
17
+ /** 凭证生效时间(秒) */
18
+ startTime: number;
19
+ /** 凭证过期时间(秒) */
20
+ expiredTime: number;
21
+ /**
22
+ * 业务路径映射
23
+ * @description 后端按业务类型返回的可写路径 map;消费方按 pathKey 选取
24
+ */
25
+ path?: Record<string, string>;
26
+ /**
27
+ * 受控目录前缀(SDK 定制场景返回)
28
+ * @description 前端在该前缀下自行拼接文件名;可与 pathKey 继续拼接子目录
29
+ */
30
+ filePath?: string;
31
+ /**
32
+ * CDN 访问域名(不含尾斜杠)
33
+ * @description SDK 定制场景返回 cdn_url;通用场景无此字段时组件按 COS Location 拼接
34
+ */
35
+ cosHost?: string;
36
+ }
37
+ /**
38
+ * 通用 COS 凭证响应(snake_case,pnt-tools / account-business)
39
+ */
40
+ interface SnakeCosCredential {
41
+ bucket: string;
42
+ region: string;
43
+ secret_id: string;
44
+ secret_key: string;
45
+ token: string;
46
+ start_time: number;
47
+ expired_time: number;
48
+ path?: Record<string, string>;
49
+ }
50
+ /**
51
+ * 内置业务场景
52
+ * @description 决定走哪个后端凭证接口
53
+ * - default: 通用上传(GET /v2/business/operation/get-cos-temp-token)
54
+ * - default_download: 通用下载/上传(POST /v2/business/operation/get-download-cos-temp-token,snake_case)
55
+ * - sdk_customize: SDK 定制服务(POST /internal/sdk_customize/upload_token,含 cdn_url/file_path)
56
+ * - ai_translate: AI 翻译服务(POST /business/aiTranslate/import/cosToken,camelCase)
57
+ */
58
+ export type CosScene = 'default' | 'default_download' | 'sdk_customize' | 'ai_translate' | (string & Record<never, never>);
59
+ /**
60
+ * 凭证获取参数
61
+ */
62
+ export interface GetCosCredentialParams {
63
+ /** 业务场景 */
64
+ scene: CosScene;
65
+ /** 业务类型路径 key(透传到后端 path map 选取;sdk_customize 场景作为受控目录下的子目录) */
66
+ pathKey?: string;
67
+ /**
68
+ * 业务上下文参数(gameId/env/updType 等,按 scene 不同传递)
69
+ * @description default/default_download 场景下 gameId 会自动映射到请求头 x-service-type,
70
+ * 其余字段透传到 query/body;ai_translate 场景整体透传到 body
71
+ */
72
+ context?: Record<string, string | number>;
73
+ /** 文件大小(字节),后端用于校验配额 */
74
+ contentLength?: number;
75
+ /** 已上传文件 URL/Key(下载场景使用) */
76
+ fileUrl?: string;
77
+ /**
78
+ * 网关 audience
79
+ * @default '/player-network'
80
+ * @description 账号管理等子系统需覆盖为对应 audience(如 '/laas-pnt');
81
+ * 消费方注入的 http 实例若已统一追加 audience,URL 上存在该参数时拦截器不会重复追加
82
+ */
83
+ audience?: string;
84
+ }
85
+ /**
86
+ * 自定义原始响应归一化函数
87
+ * @description 后端返回字段不符合内置 scene 约定时使用;入参为后端响应的原始 data
88
+ */
89
+ export type NormalizeCredential = (raw: unknown) => NormalizedCosCredential;
90
+ /**
91
+ * snake_case 凭证归一化(导出供 getCredential 自定义逃生舱复用)
92
+ * @description 消费方自行请求凭证(如项目 http 实例为非标准签名)时,
93
+ * 可在 getCredential 内调用本函数把后端 data 转为组件库统一结构
94
+ */
95
+ export declare const normalizeSnakeCredential: (raw: SnakeCosCredential) => NormalizedCosCredential;
96
+ /**
97
+ * 获取 COS 临时凭证(按 scene 路由到不同后端接口,内部归一化字段命名)
98
+ * @param http 由 PntProvider 注入的 axios 实例
99
+ * @param params 凭证获取参数
100
+ * @param customNormalize 可选的原始响应自定义归一化
101
+ */
102
+ export declare const fetchCosCredential: (http: AxiosInstance, params: GetCosCredentialParams, customNormalize?: NormalizeCredential) => Promise<NormalizedCosCredential>;
103
+ export {};
@@ -0,0 +1,137 @@
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/api/cos.ts
20
+ var cos_exports = {};
21
+ __export(cos_exports, {
22
+ fetchCosCredential: () => fetchCosCredential,
23
+ normalizeSnakeCredential: () => normalizeSnakeCredential
24
+ });
25
+ module.exports = __toCommonJS(cos_exports);
26
+ var normalizeSnake = (raw) => ({
27
+ bucket: raw.bucket,
28
+ region: raw.region,
29
+ secretId: raw.secret_id,
30
+ secretKey: raw.secret_key,
31
+ sessionToken: raw.token,
32
+ startTime: raw.start_time,
33
+ expiredTime: raw.expired_time,
34
+ path: raw.path
35
+ });
36
+ var normalizeSnakeCredential = (raw) => normalizeSnake(raw);
37
+ var normalizeAiTranslate = (raw) => {
38
+ var _a, _b;
39
+ return {
40
+ bucket: raw.bucket || "",
41
+ region: raw.region || "",
42
+ secretId: raw.secretId || "",
43
+ secretKey: raw.secretKey || "",
44
+ sessionToken: raw.token || "",
45
+ startTime: (_a = raw.startTime) != null ? _a : 0,
46
+ expiredTime: (_b = raw.expiredTime) != null ? _b : 0,
47
+ // aiTranslate path 类型为 Record<string, unknown>,这里收窄为 string
48
+ path: raw.path ? Object.fromEntries(
49
+ Object.entries(raw.path).filter(
50
+ (v) => typeof v[1] === "string"
51
+ )
52
+ ) : void 0
53
+ };
54
+ };
55
+ var normalizeSdkCustomize = (raw) => ({
56
+ bucket: raw.bucket,
57
+ region: raw.region,
58
+ secretId: raw.secret_id,
59
+ secretKey: raw.secret_key,
60
+ sessionToken: raw.token,
61
+ startTime: raw.start_time,
62
+ expiredTime: raw.expired_time,
63
+ filePath: raw.file_path,
64
+ cosHost: raw.cdn_url
65
+ });
66
+ var DEFAULT_AUDIENCE = "/player-network";
67
+ var resolveCredential = (res, normalize, customNormalize) => {
68
+ var _a, _b;
69
+ const success = res.code === 0 || res.ret === 0;
70
+ if (!success || res.data === null || typeof res.data === "undefined") {
71
+ throw new Error(
72
+ `[PntCosUploader] 获取 COS 凭证失败: code=${(_a = res.code) != null ? _a : "null"} ret=${(_b = res.ret) != null ? _b : "null"} ${res.msg || ""}`
73
+ );
74
+ }
75
+ return customNormalize ? customNormalize(res.data) : normalize(res.data);
76
+ };
77
+ var fetchCosCredential = async (http, params, customNormalize) => {
78
+ const {
79
+ scene,
80
+ context = {},
81
+ contentLength,
82
+ fileUrl,
83
+ audience = DEFAULT_AUDIENCE
84
+ } = params;
85
+ switch (scene) {
86
+ case "default": {
87
+ const { gameId, ...restContext } = context;
88
+ const res = await http.get(`/v2/business/operation/get-cos-temp-token?audience=${audience}`, {
89
+ params: {
90
+ content_length: contentLength,
91
+ ...restContext
92
+ },
93
+ headers: gameId !== void 0 ? { "x-service-type": String(gameId) } : void 0
94
+ });
95
+ return resolveCredential(res, normalizeSnake, customNormalize);
96
+ }
97
+ case "default_download": {
98
+ const { gameId, ...restContext } = context;
99
+ const res = await http.post(
100
+ `/v2/business/operation/get-download-cos-temp-token?audience=${audience}`,
101
+ {
102
+ file_url: fileUrl,
103
+ content_length: contentLength,
104
+ ...restContext
105
+ },
106
+ {
107
+ headers: gameId !== void 0 ? { "x-service-type": String(gameId) } : void 0
108
+ }
109
+ );
110
+ return resolveCredential(res, normalizeSnake, customNormalize);
111
+ }
112
+ case "sdk_customize": {
113
+ const res = await http.post("/internal/sdk_customize/upload_token", {
114
+ content_length: contentLength,
115
+ ...context
116
+ });
117
+ return resolveCredential(res, normalizeSdkCustomize, customNormalize);
118
+ }
119
+ case "ai_translate": {
120
+ const res = await http.post("/business/aiTranslate/import/cosToken", {
121
+ contentLength,
122
+ ...context
123
+ });
124
+ return resolveCredential(res, normalizeAiTranslate, customNormalize);
125
+ }
126
+ default: {
127
+ throw new Error(
128
+ `[PntCosUploader] 未支持的 scene: ${scene};请通过 getCredential 自定义`
129
+ );
130
+ }
131
+ }
132
+ };
133
+ // Annotate the CommonJS export names for ESM import in node:
134
+ 0 && (module.exports = {
135
+ fetchCosCredential,
136
+ normalizeSnakeCredential
137
+ });