@pnt-team/pnt-component-frontend 0.0.73 → 0.0.74

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 CHANGED
@@ -22,11 +22,23 @@ yarn add @pnt-team/pnt-component-frontend
22
22
 
23
23
  ## 快速开始
24
24
 
25
+ 使用前需在应用根入口用 `PntProvider` 注入 http 实例(消费方已配置好的 axios):
26
+
27
+ ```tsx
28
+ import { PntProvider } from '@pnt-team/pnt-component-frontend';
29
+ import http from '你的项目中的 axios 实例';
30
+
31
+ <PntProvider value={{ http }}>
32
+ <App />
33
+ </PntProvider>;
34
+ ```
35
+
25
36
  ```tsx
26
37
  import {
27
38
  // L1 基础展示组件
28
39
  PntImagePreview,
29
40
  // L2 业务复合组件
41
+ PntBusinessSelect,
30
42
  PntSecureBox,
31
43
  // L3 布局组件
32
44
  PntGeneralLayout,
@@ -67,9 +79,45 @@ import {
67
79
 
68
80
  ### L2 业务复合组件
69
81
 
82
+ #### PntBusinessSelect — 业务选择器组件
83
+
84
+ 统一业务下拉选择器,内置对接后端通用接口 `/api/business/select/list`,支持远程搜索、滚动分页加载、多选、场景化禁用。
85
+
86
+ **Props:**
87
+
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']>` | ❌ | - | 弹层属性透传 |
101
+
102
+ **示例:**
103
+
104
+ ```tsx
105
+ import { PntBusinessSelect } from '@pnt-team/pnt-component-frontend';
106
+
107
+ <PntBusinessSelect
108
+ scene="compliance_age"
109
+ labelFormat="name_id"
110
+ placeholder="请选择业务"
111
+ value={gameId}
112
+ onChange={(v) => setGameId(v as string)}
113
+ />;
114
+ ```
115
+
116
+ ---
117
+
70
118
  #### PntSecureBox — 高敏信息展示组件
71
119
 
72
- 统一处理和展示高敏信息,支持脱敏显示和按需获取真实数据。通过外部注入 `fetcher` 请求函数解锁敏感数据,零内部依赖。
120
+ 统一处理和展示高敏信息,支持脱敏显示和按需获取真实数据。通过 `PntProvider` 注入的 http 实例请求解锁敏感数据。
73
121
 
74
122
  **Props:**
75
123
 
@@ -80,26 +128,19 @@ import {
80
128
  | `env` | `string \| number` | ✅ | 环境标识(如 dev / test / production) |
81
129
  | `gameId` | `string \| number` | ✅ | 游戏 ID |
82
130
  | `renderDom` | `(content: string) => React.ReactNode` | ✅ | 自定义渲染函数 |
83
- | `fetcher` | `HighSecurityFetcher` | ✅ | 高敏数据请求函数(外部注入) |
84
131
  | `viewText` | `string` | ❌ | 查看按钮文案,默认 `'View'` |
85
132
  | `pageId` | `string` | ❌ | 页面 ID(用于操作日志和权限验证) |
86
133
 
87
134
  **示例:**
88
135
 
89
136
  ```tsx
90
- import http from '@/utils/request';
91
137
  import { PntSecureBox } from '@pnt-team/pnt-component-frontend';
92
138
 
93
139
  <PntSecureBox
94
140
  dataKey="userPhone"
95
- dataValue="138****8888"
96
- env="production"
141
+ dataValue="_high_security_:AAAA...:6542"
142
+ env={2}
97
143
  gameId={12345}
98
- fetcher={(params) =>
99
- http
100
- .post('/api/v3/business/game_info/high-security-decrypt', params)
101
- .then((r) => r.data)
102
- }
103
144
  renderDom={(content) => <span>{content}</span>}
104
145
  viewText="查看"
105
146
  />;
@@ -1,10 +1,13 @@
1
- // src/api/index.ts
2
- var businessSelectList = (axios, data) => axios.post("/business/select/list?audience=/player-network", data);
3
- var businessGameInfoHighSecurityDecrypt = (axios, data) => axios.post(
4
- "/v3/business/game_info/high-security-decrypt?audience=/laas-pnt",
5
- data
6
- );
7
- export {
8
- businessGameInfoHighSecurityDecrypt,
9
- businessSelectList
10
- };
1
+ /**
2
+ * 业务下拉选择列表
3
+ * @param http 由 PntProvider 注入的 axios 实例
4
+ * @param data 请求参数
5
+ */
6
+ export const businessSelectList = (axios, data) => axios.post('/business/select/list?audience=/player-network', data);
7
+
8
+ /**
9
+ * 高敏信息解锁
10
+ * @param http 由 PntProvider 注入的 axios 实例
11
+ * @param data 请求参数
12
+ */
13
+ export const businessGameInfoHighSecurityDecrypt = (axios, data) => axios.post('/v3/business/game_info/high-security-decrypt?audience=/laas-pnt', data);
@@ -0,0 +1 @@
1
+ export {};
@@ -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
- useCallback,
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
- import { jsx } from "react/jsx-runtime";
14
- var PntBusinessSelect = (props) => {
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 = "default",
64
+ scene = 'default',
20
65
  statuses,
21
66
  businessTypes,
22
67
  businessAccessMethod,
23
- labelFormat = "name",
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 = "top",
85
+ tooltipPlacement = 'top',
41
86
  className,
42
87
  style
43
88
  } = props;
44
- const { http } = usePntConfig();
45
- const requestFn = useCallback(
46
- (params) => businessSelectList(http, params),
47
- [http]
48
- );
49
- const [options, setOptions] = useState(
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
- const selectedItemsRef = useRef(/* @__PURE__ */ new Map());
59
- const buildParams = useCallback(
60
- (kw, page) => ({
61
- keyword: kw || void 0,
62
- pageNum: pagination ? page : 1,
63
- pageSize: pagination ? pageSize : 0,
64
- scene,
65
- statuses,
66
- business_types: businessTypes,
67
- business_access_method: businessAccessMethod,
68
- fields
69
- }),
70
- [
71
- scene,
72
- statuses,
73
- businessTypes,
74
- businessAccessMethod,
75
- pageSize,
76
- fields,
77
- pagination
78
- ]
79
- );
80
- const fetchData = useCallback(
81
- async (kw, page, append) => {
82
- var _a;
83
- if (loadingRef.current)
84
- return;
85
- loadingRef.current = true;
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
- [requestFn, buildParams]
101
- );
102
- const handleSearch = useCallback(
103
- (val) => {
104
- setKeyword(val);
105
- if (debounceRef.current)
106
- clearTimeout(debounceRef.current);
107
- debounceRef.current = setTimeout(() => {
108
- fetchData(val, 1, false);
109
- }, searchDebounce);
110
- },
111
- [fetchData, searchDebounce]
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
- const handlePopupVisibleChange = useCallback(
119
- (visible) => {
120
- if (!visible && keyword) {
121
- setKeyword("");
122
- fetchData("", 1, false);
123
- }
124
- },
125
- [keyword, fetchData]
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("", 1, false);
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((item) => {
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
- const formatLabel = useCallback(
145
- (item) => {
146
- if (optionLabel)
147
- return optionLabel(item);
148
- if (labelField) {
149
- return item[labelField] || item.name || "";
150
- }
151
- const name = item.name || "";
152
- const nameEn = item.name_en || "";
153
- const gameId = item.game_id || "";
154
- switch (labelFormat) {
155
- case "name_en":
156
- return nameEn || name;
157
- case "name_id":
158
- return `${name}(${gameId})`;
159
- case "name_en_id":
160
- return `${nameEn}(${gameId})`;
161
- default:
162
- return name;
163
- }
164
- },
165
- [labelFormat, labelField, optionLabel]
166
- );
167
- const isOptionDisabled = useCallback(
168
- (item) => {
169
- if (scene === "compliance_age" || scene === "custom_region") {
170
- if (item.creatable === false)
171
- return true;
172
- }
173
- return optionDisabled ? optionDisabled(item) : false;
174
- },
175
- [scene, optionDisabled]
176
- );
177
- const getOptionTooltip = useCallback(
178
- (item) => {
179
- return optionTooltip ? optionTooltip(item) : "";
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 = /* @__PURE__ */ new Map();
185
- options.forEach((item) => {
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((id) => {
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,86 @@ var PntBusinessSelect = (props) => {
210
254
  rawItem: item
211
255
  });
212
256
  } else {
213
- map.set(id, { label: id, value: id, disabled: false });
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
- const localFilter = useCallback(
220
- (filterWord, option) => {
221
- if (!filterWord)
222
- return true;
223
- const kw = filterWord.toLowerCase();
224
- const opt = option;
225
- const item = opt == null ? void 0 : opt.rawItem;
226
- if (item) {
227
- return Object.values(item).some(
228
- (v) => typeof v === "string" && v.toLowerCase().includes(kw)
229
- );
230
- }
231
- return ((opt == null ? void 0 : opt.name) || "").toLowerCase().includes(kw) || ((opt == null ? void 0 : opt.name_en) || "").toLowerCase().includes(kw) || ((opt == null ? void 0 : opt.game_id) || "").includes(kw);
232
- },
233
- []
234
- );
235
- const { Option } = Select;
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 /* @__PURE__ */ jsx(
240
- Select,
241
- {
242
- value,
243
- onChange,
244
- filterable,
245
- filter: filterable ? localFilter : void 0,
246
- onSearch: enableRemoteSearch ? handleSearch : void 0,
247
- loading: loading && options.length === 0,
248
- multiple,
249
- max,
250
- clearable,
251
- disabled,
252
- placeholder,
253
- minCollapsedNum,
254
- collapsedItems,
255
- onPopupVisibleChange: handlePopupVisibleChange,
256
- panelBottomContent: options.length > 0 && loading ? /* @__PURE__ */ jsx(Loading, { loading: true, size: "small" }) : null,
257
- popupProps: {
258
- ...popupProps,
259
- onScrollToBottom: enableScrollBottom ? handleScrollToBottom : void 0
260
- },
261
- className: className ? `pnt-business-select ${className}` : "pnt-business-select",
262
- style,
263
- children: selectOptions.map((opt) => {
264
- const item = opt.rawItem;
265
- const tip = hasTooltip && item ? getOptionTooltip(item) : "";
266
- const labelText = typeof opt.label === "string" ? opt.label : String(opt.label ?? "");
267
- return /* @__PURE__ */ jsx(
268
- Option,
269
- {
270
- value: opt.value,
271
- label: labelText,
272
- disabled: opt.disabled,
273
- children: tip ? /* @__PURE__ */ jsx(Tooltip, { content: tip, placement: tooltipPlacement, children: /* @__PURE__ */ jsx("span", { className: "pnt-business-select-option-label", children: opt.label }) }) : opt.label
274
- },
275
- opt.value
276
- );
277
- })
278
- }
279
- );
280
- };
281
- var PntBusinessSelect_default = PntBusinessSelect;
282
- export {
283
- PntBusinessSelect_default as default
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
+ onScrollToBottom: enableScrollBottom ? handleScrollToBottom : undefined
313
+ },
314
+ className: className ? `pnt-business-select ${className}` : 'pnt-business-select',
315
+ style: style,
316
+ children: selectOptions.map(opt => {
317
+ const item = opt.rawItem;
318
+ const tip = hasTooltip && item ? getOptionTooltip(item) : '';
319
+ // label 仅用纯文本字符串(用于 tag 显示与过滤匹配),复杂渲染走 children
320
+ const labelText = typeof opt.label === 'string' ? opt.label : String(opt.label ?? '');
321
+ // Tooltip 必须放在 Option 内部:Select 通过直接子节点识别 Option,
322
+ // 外层包裹会破坏选项注册;且禁用项的 hover 事件由内部元素接收才稳定
323
+ return /*#__PURE__*/_jsx(Option, {
324
+ value: opt.value,
325
+ label: labelText,
326
+ disabled: opt.disabled,
327
+ children: tip ? /*#__PURE__*/_jsx(Tooltip, {
328
+ content: tip,
329
+ placement: tooltipPlacement,
330
+ children: /*#__PURE__*/_jsx("span", {
331
+ className: "pnt-business-select-option-label",
332
+ children: opt.label
333
+ })
334
+ }) : opt.label
335
+ }, opt.value);
336
+ })
337
+ });
284
338
  };
339
+ export default PntBusinessSelect;
@@ -1,9 +1,41 @@
1
- // src/components/PntGeneralLayout/index.tsx
2
- import { ArrowLeftIcon } from "tdesign-icons-react";
3
- import { Layout } from "tdesign-react";
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, jsxs } from "react/jsx-runtime";
6
- var PntGeneralLayout = (prop) => {
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 { Header, Footer } = Layout;
20
- return /* @__PURE__ */ jsxs(Layout, { className: `pnt-general-layout${className ? " " + className : ""}`, children: [
21
- headerTitle && /* @__PURE__ */ jsxs(Header, { children: [
22
- /* @__PURE__ */ jsxs("span", { className: "header-title", children: [
23
- showBackButton && /* @__PURE__ */ jsx(
24
- ArrowLeftIcon,
25
- {
26
- className: "goback-icon",
27
- size: "24px",
28
- onClick: onBackButtonClick
29
- }
30
- ),
31
- /* @__PURE__ */ jsx("span", { className: "title", children: headerTitle })
32
- ] }),
33
- operation && /* @__PURE__ */ jsx("span", { className: "header-operation", children: operation })
34
- ] }),
35
- subHeaderContent && /* @__PURE__ */ jsx("div", { className: "general-layout-sub-header", children: subHeaderContent }),
36
- /* @__PURE__ */ jsx(
37
- "div",
38
- {
39
- className: "general-layout-body",
40
- ref: bodyRef,
41
- style: {
42
- height: `calc(100%${headerTitle && " - 72px"}${footer && " - 72px"})`,
43
- ...bodyStyle
44
- },
45
- children
46
- }
47
- ),
48
- footer && /* @__PURE__ */ jsx(Footer, { children: footer })
49
- ] });
50
- };
51
- var PntGeneralLayout_default = PntGeneralLayout;
52
- export {
53
- PntGeneralLayout_default as default
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;
@@ -1,68 +1,120 @@
1
- // src/components/PntImagePreview/index.tsx
2
1
  import uploadImg from "../../assets/images/uploadImg.png";
3
- import { BrowseIcon, DeleteIcon, LoadingIcon } from "tdesign-icons-react";
4
- import { Image, ImageViewer } from "tdesign-react";
2
+ import React from 'react';
3
+ import { BrowseIcon, DeleteIcon, LoadingIcon } from 'tdesign-icons-react';
4
+ import { Image, ImageViewer } from 'tdesign-react';
5
5
  import "./index.scss";
6
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
- var PntImagePreview = ({
6
+ import { jsx as _jsx } from "react/jsx-runtime";
7
+ import { jsxs as _jsxs } from "react/jsx-runtime";
8
+ import { Fragment as _Fragment } from "react/jsx-runtime";
9
+ /**
10
+ * 上传图片查看器组件
11
+ * @description 用于展示上传的图片,支持预览、删除功能和上传状态显示
12
+ * @param {PntImagePreviewProps} props - 组件属性
13
+ * @param {string | undefined} props.iconUrl - 当前图片组件 URL
14
+ * @param {boolean} props.loading - 上传加载状态
15
+ * @param {string} [props.defaultImg] - 默认图片
16
+ * @param {object} [props.params] - 自定义扩展参数
17
+ * @param {function} [props.onRemove] - 删除回调函数
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * // 基础用法
22
+ * <UploadImageViewer
23
+ * iconUrl={imageUrl}
24
+ * loading={uploading}
25
+ * onRemove={handleRemove}
26
+ * />
27
+ *
28
+ * // 带默认图片
29
+ * <UploadImageViewer
30
+ * iconUrl={imageUrl}
31
+ * loading={false}
32
+ * defaultImg="/path/to/default.png"
33
+ * onRemove={handleRemove}
34
+ * />
35
+ *
36
+ * // 自定义样式
37
+ * <UploadImageViewer
38
+ * iconUrl={imageUrl}
39
+ * loading={uploading}
40
+ * params={{ style: { width: '200px', height: '200px' } }}
41
+ * onRemove={handleRemove}
42
+ * />
43
+ * ```
44
+ *
45
+ * @returns {ReactElement} 上传图片查看器组件
46
+ */
47
+ const PntImagePreview = ({
8
48
  iconUrl,
9
49
  loading,
10
50
  defaultImg,
11
51
  params,
12
52
  onRemove
13
53
  }) => {
14
- const src = (iconUrl == null ? void 0 : iconUrl.length) ? iconUrl : defaultImg || uploadImg;
15
- const LoadingContent = (isImageLoading) => /* @__PURE__ */ jsx(
16
- "div",
17
- {
18
- className: isImageLoading ? "pnt-image-preview-progress" : "pnt-image-preview-upload-progress",
19
- style: params == null ? void 0 : params.style,
20
- children: /* @__PURE__ */ jsxs("div", { className: "pnt-image-preview-loading", children: [
21
- /* @__PURE__ */ jsx(LoadingIcon, { size: "18px", name: "loading" }),
22
- /* @__PURE__ */ jsx("p", { children: "上传中" })
23
- ] })
24
- }
25
- );
26
- return /* @__PURE__ */ jsx(
27
- "div",
28
- {
29
- className: "pnt-image-preview-wrapper",
30
- onClick: (e) => {
31
- if ((iconUrl == null ? void 0 : iconUrl.length) || loading) {
32
- e.stopPropagation();
33
- }
34
- },
35
- children: loading ? LoadingContent(false) : /* @__PURE__ */ jsx(
36
- ImageViewer,
37
- {
38
- images: [src],
39
- closeOnOverlay: true,
40
- trigger: ({ open }) => {
41
- const mask = /* @__PURE__ */ jsxs("div", { className: "pnt-image-preview-mask", children: [
42
- /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(BrowseIcon, { onClick: open, size: "16px", name: "browse" }) }),
43
- /* @__PURE__ */ jsx("span", { className: "pnt-image-preview-divider" }),
44
- /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(DeleteIcon, { onClick: onRemove, size: "16px", name: "delete" }) })
45
- ] });
46
- return /* @__PURE__ */ jsx(
47
- Image,
48
- {
49
- alt: "",
50
- src,
51
- loading: LoadingContent(true),
52
- overlayContent: iconUrl ? mask : /* @__PURE__ */ jsx(Fragment, {}),
53
- overlayTrigger: "hover",
54
- fit: "contain",
55
- className: "pnt-image-preview",
56
- style: params == null ? void 0 : params.style
57
- }
58
- );
59
- }
60
- }
61
- )
62
- }
63
- );
64
- };
65
- var PntImagePreview_default = PntImagePreview;
66
- export {
67
- PntImagePreview_default as default
54
+ const src = iconUrl?.length ? iconUrl : defaultImg || uploadImg;
55
+
56
+ /**
57
+ * 加载中组件
58
+ * @param isImageLoading - 是否为图片加载状态
59
+ * @description 统一使用一种loading效果,避免两种加载风格
60
+ * @returns 加载状态的 JSX 元素
61
+ */
62
+ const LoadingContent = isImageLoading => /*#__PURE__*/_jsx("div", {
63
+ className: isImageLoading ? 'pnt-image-preview-progress' : 'pnt-image-preview-upload-progress',
64
+ style: params?.style,
65
+ children: /*#__PURE__*/_jsxs("div", {
66
+ className: "pnt-image-preview-loading",
67
+ children: [/*#__PURE__*/_jsx(LoadingIcon, {
68
+ size: "18px",
69
+ name: 'loading'
70
+ }), /*#__PURE__*/_jsx("p", {
71
+ children: "\u4E0A\u4F20\u4E2D"
72
+ })]
73
+ })
74
+ });
75
+ return /*#__PURE__*/_jsx("div", {
76
+ className: "pnt-image-preview-wrapper",
77
+ onClick: e => {
78
+ if (iconUrl?.length || loading) {
79
+ e.stopPropagation(); // 图片已上传 或 加载中 阻止上传行为
80
+ }
81
+ },
82
+ children: loading ? LoadingContent(false) : /*#__PURE__*/_jsx(ImageViewer, {
83
+ images: [src],
84
+ closeOnOverlay: true,
85
+ trigger: ({
86
+ open
87
+ }) => {
88
+ const mask = /*#__PURE__*/_jsxs("div", {
89
+ className: "pnt-image-preview-mask",
90
+ children: [/*#__PURE__*/_jsx("span", {
91
+ children: /*#__PURE__*/_jsx(BrowseIcon, {
92
+ onClick: open,
93
+ size: "16px",
94
+ name: 'browse'
95
+ })
96
+ }), /*#__PURE__*/_jsx("span", {
97
+ className: "pnt-image-preview-divider"
98
+ }), /*#__PURE__*/_jsx("span", {
99
+ children: /*#__PURE__*/_jsx(DeleteIcon, {
100
+ onClick: onRemove,
101
+ size: "16px",
102
+ name: 'delete'
103
+ })
104
+ })]
105
+ });
106
+ return /*#__PURE__*/_jsx(Image, {
107
+ alt: '',
108
+ src: src,
109
+ loading: LoadingContent(true),
110
+ overlayContent: iconUrl ? mask : /*#__PURE__*/_jsx(_Fragment, {}),
111
+ overlayTrigger: "hover",
112
+ fit: "contain",
113
+ className: "pnt-image-preview",
114
+ style: params?.style
115
+ });
116
+ }
117
+ })
118
+ });
68
119
  };
120
+ export default PntImagePreview;
@@ -1,38 +1,75 @@
1
- // src/components/PntSecureBox/index.tsx
2
1
  import { useHighSecurity } from "../../hooks/useHighSecurity";
3
2
  import { isSecurityData } from "../../utils";
3
+ import React from 'react';
4
4
  import "./index.scss";
5
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
- var PntSecureBox = (prop) => {
7
- const { dataKey, dataValue, env, gameId, renderDom, viewText, pageId } = prop;
8
- const { securityData, requestSecurityData } = useHighSecurity();
9
- return /* @__PURE__ */ jsx("div", { className: "pnt-secure-box", children: /* @__PURE__ */ jsxs(Fragment, { children: [
10
- renderDom(
11
- isSecurityData(dataValue) ? securityData[dataKey] || "******" : dataValue
12
- ),
13
- isSecurityData(dataValue) && !securityData.hasOwnProperty(dataKey) && /* @__PURE__ */ jsx(
14
- "span",
15
- {
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ import { Fragment as _Fragment } from "react/jsx-runtime";
7
+ import { jsxs as _jsxs } from "react/jsx-runtime";
8
+ /**
9
+ * 高敏信息展示统一组件
10
+ * @description 用于统一处理和展示高敏信息,支持脱敏显示和按需获取真实数据。
11
+ * 通过 PntProvider 注入 http 实例,对接 `/api/v3/business/game_info/high-security-decrypt`。
12
+ *
13
+ * @param {PntSecureBoxProps} props - 组件属性
14
+ * @param {string} props.dataKey - 高敏信息的唯一标识 key
15
+ * @param {string} props.dataValue - 高敏信息的实际值
16
+ * @param {string | number} props.env - 环境标识
17
+ * @param {string | number} props.gameId - 游戏 ID
18
+ * @param {(content: string) => React.ReactNode} props.renderDom - 自定义渲染函数
19
+ * @param {string} [props.viewText] - 自定义查看按钮文案
20
+ * @param {string} [props.pageId] - 页面 ID
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * import { PntSecureBox } from '@pnt-team/pnt-component-frontend';
25
+ *
26
+ * <PntSecureBox
27
+ * dataKey="userPhone"
28
+ * dataValue="_high_security_:AAAA...:6542"
29
+ * env={2}
30
+ * gameId={12345}
31
+ * renderDom={(content) => <span>{content}</span>}
32
+ * viewText="查看"
33
+ * />
34
+ * ```
35
+ *
36
+ * @returns {ReactElement} 高敏信息展示组件
37
+ */
38
+ const PntSecureBox = prop => {
39
+ const {
40
+ dataKey,
41
+ dataValue,
42
+ env,
43
+ gameId,
44
+ renderDom,
45
+ viewText,
46
+ pageId
47
+ } = prop;
48
+
49
+ // 通过 PntProvider 注入的 http 实例请求高敏数据
50
+ const {
51
+ securityData,
52
+ requestSecurityData
53
+ } = useHighSecurity();
54
+ return /*#__PURE__*/_jsx("div", {
55
+ className: "pnt-secure-box",
56
+ children: /*#__PURE__*/_jsxs(_Fragment, {
57
+ children: [renderDom(isSecurityData(dataValue) ? securityData[dataKey] || '******' : dataValue), isSecurityData(dataValue) && !securityData.hasOwnProperty(dataKey) && /*#__PURE__*/_jsx("span", {
16
58
  className: "view-icon",
17
59
  onClick: () => {
18
60
  requestSecurityData({
19
61
  env,
20
62
  gameId,
21
63
  pageId,
22
- param: [
23
- {
24
- key: dataKey,
25
- value: dataValue
26
- }
27
- ]
64
+ param: [{
65
+ key: dataKey,
66
+ value: dataValue
67
+ }]
28
68
  });
29
69
  },
30
- children: viewText || "View"
31
- }
32
- )
33
- ] }) });
34
- };
35
- var PntSecureBox_default = PntSecureBox;
36
- export {
37
- PntSecureBox_default as default
70
+ children: viewText || 'View'
71
+ })]
72
+ })
73
+ });
38
74
  };
75
+ export default PntSecureBox;
package/lib/esm/config.js CHANGED
@@ -1,17 +1,37 @@
1
- // src/config.tsx
2
- import { createContext, useContext } from "react";
3
- var PntContext = createContext(null);
4
- var PntProvider = PntContext.Provider;
5
- var usePntConfig = () => {
1
+ import { createContext, useContext } from 'react';
2
+
3
+ /**
4
+ * 统一响应结构(与后端 commonEesType 对齐)
5
+ */
6
+
7
+ /**
8
+ * 组件库运行时配置:由消费方通过 PntProvider 注入
9
+ */
10
+
11
+ const PntContext = /*#__PURE__*/createContext(null);
12
+
13
+ /**
14
+ * 配置 Provider:消费方在应用根入口包裹一次即可
15
+ * @example
16
+ * ```tsx
17
+ * import { PntProvider } from '@pnt-team/pnt-component-frontend';
18
+ * import http from './utils/request';
19
+ *
20
+ * <PntProvider value={{ http }}>
21
+ * <App />
22
+ * </PntProvider>
23
+ * ```
24
+ */
25
+ export const PntProvider = PntContext.Provider;
26
+
27
+ /**
28
+ * 获取组件库运行时配置(内部使用)
29
+ * @internal
30
+ */
31
+ export const usePntConfig = () => {
6
32
  const cfg = useContext(PntContext);
7
33
  if (!cfg) {
8
- throw new Error(
9
- "[pnt-component-frontend] 未注入配置:请在应用根入口用 <PntProvider value={{ http }}> 包裹"
10
- );
34
+ throw new Error('[pnt-component-frontend] 未注入配置:请在应用根入口用 <PntProvider value={{ http }}> 包裹');
11
35
  }
12
36
  return cfg;
13
- };
14
- export {
15
- PntProvider,
16
- usePntConfig
17
- };
37
+ };
@@ -1,40 +1,47 @@
1
- // src/hooks/useHighSecurity.ts
2
1
  import { businessGameInfoHighSecurityDecrypt } from "../api";
3
2
  import { usePntConfig } from "../config";
4
- import { useState } from "react";
5
- var securityCache = /* @__PURE__ */ new Map();
6
- var useHighSecurity = () => {
7
- const { http } = usePntConfig();
8
- const [securityData, setSecurityData] = useState(
9
- () => {
10
- const cached = {};
11
- securityCache.forEach((value, key) => {
12
- cached[key] = value;
13
- });
14
- return cached;
15
- }
16
- );
3
+ import { useState } from 'react';
4
+
5
+ /** 高敏数据请求参数(组件层友好命名) */
6
+
7
+ const securityCache = new Map();
8
+
9
+ /**
10
+ * 高敏数据解锁 Hook(PntSecureBox 内部依赖)
11
+ * @internal 不对外导出,归 PntSecureBox 组件内部使用
12
+ */
13
+ export const useHighSecurity = () => {
14
+ const {
15
+ http
16
+ } = usePntConfig();
17
+ const [securityData, setSecurityData] = useState(() => {
18
+ const cached = {};
19
+ securityCache.forEach((value, key) => {
20
+ cached[key] = value;
21
+ });
22
+ return cached;
23
+ });
17
24
  const requestSecurityData = async ({
18
25
  gameId,
19
26
  param,
20
27
  env,
21
28
  pageId
22
29
  }) => {
30
+ // 适配后端 BusinessGameInfoHighSecurityDecryptBody 结构
23
31
  const body = {
24
32
  game_id: Number(gameId),
25
33
  env: Number(env),
26
34
  page_id: pageId || location.pathname,
27
- param: param.map(
28
- (i) => ({
29
- key: i.key,
30
- value: i.value
31
- })
32
- )
35
+ param: param.map(i => ({
36
+ key: i.key,
37
+ value: i.value
38
+ }))
33
39
  };
34
40
  const result = await businessGameInfoHighSecurityDecrypt(http, body);
41
+ // 适配 commonEesType:code === 0 视为成功
35
42
  if (result.code === 0 && result.data) {
36
43
  const data = result.data;
37
- data.forEach((item) => {
44
+ data.forEach(item => {
38
45
  securityCache.set(item.key, item.value);
39
46
  });
40
47
  setSecurityData(Object.fromEntries(securityCache));
@@ -44,7 +51,4 @@ var useHighSecurity = () => {
44
51
  securityData,
45
52
  requestSecurityData
46
53
  };
47
- };
48
- export {
49
- useHighSecurity
50
- };
54
+ };
package/lib/esm/index.js CHANGED
@@ -1,17 +1,18 @@
1
- // src/index.ts
2
- import {
3
- PntProvider,
4
- usePntConfig
5
- } from "./config";
6
- import { default as default2 } from "./components/PntImagePreview";
7
- import { default as default3 } from "./components/PntBusinessSelect";
8
- import { default as default4 } from "./components/PntSecureBox";
9
- import { default as default5 } from "./components/PntGeneralLayout";
10
- export {
11
- default3 as PntBusinessSelect,
12
- default5 as PntGeneralLayout,
13
- default2 as PntImagePreview,
14
- PntProvider,
15
- default4 as PntSecureBox,
16
- usePntConfig
17
- };
1
+ // ==========================================
2
+ // pnt-component-frontend 统一出口
3
+ // 只保留业务语义组件:有视觉、有交互、有业务含义
4
+ // hooks / 纯工具 / API wrapper 归各项目自己维护
5
+ // ==========================================
6
+
7
+ // ---------- 配置 Provider(消费方根入口注入 http 实例) ----------
8
+ export { PntProvider, usePntConfig } from "./config";
9
+
10
+ // ---------- L1 基础展示组件 ----------
11
+ export { default as PntImagePreview } from "./components/PntImagePreview";
12
+
13
+ // ---------- L2 业务复合组件 ----------
14
+ export { default as PntBusinessSelect } from "./components/PntBusinessSelect";
15
+ export { default as PntSecureBox } from "./components/PntSecureBox";
16
+
17
+ // ---------- L3 布局组件 ----------
18
+ export { default as PntGeneralLayout } from "./components/PntGeneralLayout";
@@ -1,16 +1,17 @@
1
- // src/utils/const.ts
2
- var SUB_APP_PATHNAME = {
3
- ACCOUNT: "/laas",
4
- SDK: "/intlsdk",
5
- NOTICE: "/notice",
6
- CONFIGFROM: "/configform",
7
- CONFIGTHEME: "/configtheme",
8
- REVIEWLIST: "/approvalservices",
9
- INTERNALTOOLS: "/internaltools",
10
- REPORTS: "/reports"
1
+ // 子系统页面的路由
2
+ export const SUB_APP_PATHNAME = {
3
+ ACCOUNT: '/laas',
4
+ SDK: '/intlsdk',
5
+ NOTICE: '/notice',
6
+ CONFIGFROM: '/configform',
7
+ CONFIGTHEME: '/configtheme',
8
+ REVIEWLIST: '/approvalservices',
9
+ INTERNALTOOLS: '/internaltools',
10
+ REPORTS: '/reports'
11
11
  };
12
- var audienceSuffix = "-pnt";
13
- var AUDIENCE_MAPPING = {
12
+ const audienceSuffix = '-pnt';
13
+ // 子系统的audienceMapping
14
+ export const AUDIENCE_MAPPING = {
14
15
  [SUB_APP_PATHNAME.ACCOUNT]: `/laas${audienceSuffix}`,
15
16
  [SUB_APP_PATHNAME.SDK]: `/intl-sdk${audienceSuffix}`,
16
17
  [SUB_APP_PATHNAME.NOTICE]: `/notice${audienceSuffix}`,
@@ -19,8 +20,4 @@ var AUDIENCE_MAPPING = {
19
20
  [SUB_APP_PATHNAME.REVIEWLIST]: `/approval-services${audienceSuffix}`,
20
21
  [SUB_APP_PATHNAME.INTERNALTOOLS]: `/internaltools${audienceSuffix}`,
21
22
  [SUB_APP_PATHNAME.REPORTS]: `/reports${audienceSuffix}`
22
- };
23
- export {
24
- AUDIENCE_MAPPING,
25
- SUB_APP_PATHNAME
26
- };
23
+ };
@@ -1,19 +1,22 @@
1
- // src/utils/index.ts
2
- var isSecurityData = (val) => {
1
+ /**
2
+ * 判断是否是高敏信息逻辑
3
+ * @param {*} val
4
+ * @returns
5
+ */
6
+ export const isSecurityData = val => {
3
7
  const str = String(val);
4
- if (!str.includes("_high_security_:"))
5
- return false;
6
- const rsp = str.split(":");
7
- const calcStr = rsp.slice(0, -1).join(":");
8
+ if (!str.includes('_high_security_:')) return false;
9
+
10
+ // 符合加密条件 开始解密
11
+ const rsp = str.split(':');
12
+ const calcStr = rsp.slice(0, -1).join(':');
8
13
  const calcNum = parseInt(rsp[rsp.length - 1], 10);
9
- if (isNaN(calcNum))
10
- return false;
14
+
15
+ // 错误处理
16
+ if (isNaN(calcNum)) return false;
11
17
  let num = 0;
12
18
  for (const item of calcStr) {
13
19
  num += item.charCodeAt(0);
14
20
  }
15
21
  return num === calcNum;
16
- };
17
- export {
18
- isSecurityData
19
- };
22
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnt-team/pnt-component-frontend",
3
- "version": "0.0.73",
3
+ "version": "0.0.74",
4
4
  "description": "A react library developed with dumi",
5
5
  "license": "MIT",
6
6
  "exports": {