@pnt-team/pnt-component-frontend 0.0.111 → 0.0.116

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 (37) hide show
  1. package/README.md +29 -30
  2. package/lib/cjs/api/index.d.ts +9 -1
  3. package/lib/cjs/api/index.js +6 -0
  4. package/lib/cjs/api/types/index.d.ts +43 -6
  5. package/lib/cjs/components/PntBusinessSelect/index.d.ts +0 -5
  6. package/lib/cjs/components/PntBusinessSelect/index.js +1 -11
  7. package/lib/cjs/components/PntRegionSelect/ImportDialog.d.ts +25 -0
  8. package/lib/cjs/components/PntRegionSelect/ImportDialog.js +244 -0
  9. package/lib/cjs/components/PntRegionSelect/index.d.ts +34 -0
  10. package/lib/cjs/components/PntRegionSelect/index.js +387 -0
  11. package/lib/cjs/components/PntRegionSelect/index.scss +166 -0
  12. package/lib/cjs/components/PntRegionSelect/types.d.ts +160 -0
  13. package/lib/cjs/components/PntRegionSelect/types.js +51 -0
  14. package/lib/cjs/components/PntSecureBox/index.scss +3 -0
  15. package/lib/cjs/config.d.ts +17 -0
  16. package/lib/cjs/index.d.ts +5 -1
  17. package/lib/cjs/index.js +10 -0
  18. package/lib/esm/api/index.d.ts +9 -1
  19. package/lib/esm/api/index.js +10 -1
  20. package/lib/esm/api/types/index.d.ts +43 -6
  21. package/lib/esm/components/PntBusinessSelect/index.d.ts +0 -5
  22. package/lib/esm/components/PntBusinessSelect/index.js +1 -3
  23. package/lib/esm/components/PntRegionSelect/ImportDialog.d.ts +25 -0
  24. package/lib/esm/components/PntRegionSelect/ImportDialog.js +241 -0
  25. package/lib/esm/components/PntRegionSelect/index.d.ts +34 -0
  26. package/lib/esm/components/PntRegionSelect/index.js +368 -0
  27. package/lib/esm/components/PntRegionSelect/index.scss +166 -0
  28. package/lib/esm/components/PntRegionSelect/types.d.ts +160 -0
  29. package/lib/esm/components/PntRegionSelect/types.js +49 -0
  30. package/lib/esm/components/PntSecureBox/index.scss +3 -0
  31. package/lib/esm/config.d.ts +17 -0
  32. package/lib/esm/config.js +4 -0
  33. package/lib/esm/index.d.ts +5 -1
  34. package/lib/esm/index.js +3 -0
  35. package/lib/umd/pnt-component-frontend.min.css +1 -1
  36. package/lib/umd/pnt-component-frontend.min.js +1 -1
  37. package/package.json +1 -1
package/README.md CHANGED
@@ -121,36 +121,35 @@ import {
121
121
 
122
122
  **Props:**
123
123
 
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` | ❌ | - | 自定义样式 |
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
+ | `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` | ❌ | - | 自定义样式 |
154
153
 
155
154
  **示例:**
156
155
 
@@ -1,5 +1,5 @@
1
1
  import type { commonEesType, PntHttpLike } from "../config";
2
- import { BusinessGameInfoHighSecurityDecryptBody, BusinessGameInfoHighSecurityDecryptResponse, BusinessSelectListBody, BusinessSelectListResponse } from './types/index';
2
+ import { BusinessGameInfoHighSecurityDecryptBody, BusinessGameInfoHighSecurityDecryptResponse, BusinessRegionQueryBody, BusinessRegionQueryResponse, BusinessSelectListBody, BusinessSelectListResponse } from './types/index';
3
3
  /**
4
4
  * 业务下拉选择列表
5
5
  * @param http 由 PntProvider 注入的 axios 实例
@@ -13,3 +13,11 @@ export declare const businessSelectList: (axios: PntHttpLike, data: BusinessSele
13
13
  * @param url 自定义接口 URL(可选,覆盖默认值)
14
14
  */
15
15
  export declare const businessGameInfoHighSecurityDecrypt: (axios: PntHttpLike, data: BusinessGameInfoHighSecurityDecryptBody, url?: string) => Promise<commonEesType<BusinessGameInfoHighSecurityDecryptResponse>>;
16
+ /**
17
+ * 业务区域查询
18
+ * @param axios 由 PntProvider 注入的 axios 实例
19
+ * @param data 请求参数;不传时不带任何参数,返回全部国家区域(251 个);
20
+ * 传 game_id 时返回该业务最终生效的区域
21
+ * @param url 自定义接口 URL(可选,覆盖默认值)
22
+ */
23
+ export declare const businessRegionQuery: (axios: PntHttpLike, data?: BusinessRegionQueryBody, url?: string) => Promise<commonEesType<BusinessRegionQueryResponse>>;
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  var api_exports = {};
21
21
  __export(api_exports, {
22
22
  businessGameInfoHighSecurityDecrypt: () => businessGameInfoHighSecurityDecrypt,
23
+ businessRegionQuery: () => businessRegionQuery,
23
24
  businessSelectList: () => businessSelectList
24
25
  });
25
26
  module.exports = __toCommonJS(api_exports);
@@ -28,8 +29,13 @@ var businessGameInfoHighSecurityDecrypt = (axios, data, url) => axios.post(
28
29
  url || "/v3/business/game_info/high-security-decrypt?audience=/laas-pnt",
29
30
  data
30
31
  );
32
+ var businessRegionQuery = (axios, data, url) => axios.post(
33
+ url || "/api/v5/business/region/query?audience=/laas-pnt",
34
+ data
35
+ );
31
36
  // Annotate the CommonJS export names for ESM import in node:
32
37
  0 && (module.exports = {
33
38
  businessGameInfoHighSecurityDecrypt,
39
+ businessRegionQuery,
34
40
  businessSelectList
35
41
  });
@@ -4,23 +4,31 @@
4
4
  * 数据来源: https://api-pnt.intlgame.com
5
5
  */
6
6
  export interface BusinessSelectListBody {
7
- /** 业务接入类型:1-纯SDK,2-LI PASS,3-工作室品牌账号 */
8
- business_access_method?: number;
9
- /** 业务类型列表:1-正式业务,2-测试业务;SmartLink业务始终不返回 */
7
+ /** 业务类型列表,仅 game_list、game_list_option 场景支持:1-正式业务,2-测试业务 */
10
8
  business_types?: number[];
9
+ /** 云控类型列表,仅 game_list、game_list_option 场景支持:1-INTL Project,2-LI PASS Project */
10
+ cloudCtrlType?: number[];
11
+ /** 集群类型列表,仅 game_list、game_list_option 场景支持:1-公有,2-私有,3-未配置 */
12
+ clusterTypes?: number[];
11
13
  /** 需要返回的字段;未传时使用场景默认字段,仅支持服务端白名单字段 */
12
14
  fields?: string[];
13
- /** 指定业务ID列表;与 Keyword 同时传入时取交集 */
15
+ /** 业务ID列表,仅 game_list、game_list_option 场景支持 */
14
16
  game_ids?: string[];
15
17
  /** 业务ID、中文名称或英文名称的模糊匹配关键字 */
16
18
  keyword?: string;
19
+ /** 登录器类型列表,仅 game_list、game_list_option 场景支持 */
20
+ lander_type?: number[];
21
+ /** LI PASS 开关列表,仅 game_list、game_list_option 场景支持:1-开启,2-未开启 */
22
+ lipSwitches?: number[];
17
23
  /** 当前页 */
18
24
  pageNum?: number;
19
25
  /** 每页条数 */
20
26
  pageSize?: number;
21
- /** 下拉场景:default-通用业务,sdk-SDK定制,compliance_age-合规年龄,custom_region-自定义合规地域 */
27
+ /** 平台列表,仅 game_list、game_list_option 场景支持 */
28
+ platform?: string[];
29
+ /** 下拉场景:game_list-业务列表,game_list_option-业务选择,compliance_age-合规年龄,custom_region-自定义合规地域,sdk-SDK定制 */
22
30
  scene?: string;
23
- /** 业务状态列表:3-线上运营,4-接入中,5-中途结项,6-已下线 */
31
+ /** 业务状态列表,仅 game_list、game_list_option 场景支持:3-线上运营,4-接入中,5-中途结项,6-已下线 */
24
32
  statuses?: number[];
25
33
  /** 总条数 */
26
34
  total?: number;
@@ -34,6 +42,8 @@ export interface BusinessSelectListResponseListItem {
34
42
  business_type?: number;
35
43
  /** 当前场景是否可创建;仅 compliance_age、custom_region 场景有意义 */
36
44
  creatable?: boolean;
45
+ /** 当前业务是否禁用合规年龄认证;仅 compliance_age 场景有意义 */
46
+ disabled?: boolean;
37
47
  /** 业务ID */
38
48
  game_id?: string;
39
49
  /** 业务接口开发负责人;SDK场景可返回 */
@@ -78,3 +88,30 @@ export interface BusinessGameInfoHighSecurityDecryptBody {
78
88
  game_id: number;
79
89
  }
80
90
  export type BusinessGameInfoHighSecurityDecryptResponse = Record<string, unknown>[];
91
+ export interface BusinessRegionQueryBody {
92
+ /** GameID 目标业务 ID;取值 > 0 时返回该业务实际生效的国家区域;
93
+ 取值 <= 0 或未传时返回国家区域全量字典;与当前登录业务是否一致不做强校验(跨业务查询合法)。 */
94
+ game_id?: number;
95
+ }
96
+ export interface BusinessRegionQueryResponseRegionListItem {
97
+ /** ID 区域主键 ID。 */
98
+ id?: number;
99
+ /** IsEEARegion 是否 EEA 区域,取值:0=否,1=是。 */
100
+ is_eea_region?: number;
101
+ /** IsSecretRegion 是否特殊合规区域,取值:1=默认区域,2=特殊区域。 */
102
+ is_secret_region?: number;
103
+ /** RegionA2Code 二位字母码。 */
104
+ region_a2_code?: string;
105
+ /** RegionA3Code 三位字母码。 */
106
+ region_a3_code?: string;
107
+ /** RegionCH 中文名称。 */
108
+ region_ch?: string;
109
+ /** RegionCode 数字码,按数据库原始值返回,不做前导零转换。 */
110
+ region_code?: string;
111
+ /** RegionEN 英文名称。 */
112
+ region_en?: string;
113
+ }
114
+ export interface BusinessRegionQueryResponse {
115
+ /** RegionList 国家区域列表:未携带 game_id 时为全量字典,携带 game_id 时为该业务实际生效区域;均按 id 升序。 */
116
+ region_list?: BusinessRegionQueryResponseRegionListItem[];
117
+ }
@@ -37,11 +37,6 @@ interface PntBusinessSelectProps {
37
37
  * @description 1-正式业务,2-测试业务
38
38
  */
39
39
  businessTypes?: number[];
40
- /**
41
- * 业务接入类型筛选
42
- * @description 1-纯SDK,2-LI PASS,3-工作室品牌账号
43
- */
44
- businessAccessMethod?: number;
45
40
  /**
46
41
  * 标签格式
47
42
  * @description name-中文名,name_en-英文名,name_id-中文名(ID),name_en_id-英文名(ID)
@@ -36,7 +36,6 @@ var PntBusinessSelect = (props) => {
36
36
  scene = "default",
37
37
  statuses,
38
38
  businessTypes,
39
- businessAccessMethod,
40
39
  labelFormat = "name",
41
40
  labelField,
42
41
  optionLabel,
@@ -94,19 +93,10 @@ var PntBusinessSelect = (props) => {
94
93
  scene,
95
94
  statuses,
96
95
  business_types: businessTypes,
97
- business_access_method: businessAccessMethod,
98
96
  fields
99
97
  }),
100
98
  // eslint-disable-next-line react-hooks/exhaustive-deps
101
- [
102
- scene,
103
- statusesKey,
104
- businessTypesKey,
105
- businessAccessMethod,
106
- pageSize,
107
- fieldsKey,
108
- pagination
109
- ]
99
+ [scene, statusesKey, businessTypesKey, pageSize, fieldsKey, pagination]
110
100
  );
111
101
  const fetchData = (0, import_react.useCallback)(
112
102
  async (kw, page, append) => {
@@ -0,0 +1,25 @@
1
+ import { FC } from 'react';
2
+ import type { PntRegionSelectTexts, RegionOption } from './types';
3
+ /** 解析后单行状态 */
4
+ export type ImportRowStatus = 'pending' | 'exists' | 'unmatched';
5
+ export interface ImportRow {
6
+ /** 用户输入的原始文本 */
7
+ region: string;
8
+ status: ImportRowStatus;
9
+ }
10
+ interface ImportDialogProps {
11
+ visible: boolean;
12
+ options: RegionOption[];
13
+ currentValue: string[];
14
+ texts: Required<PntRegionSelectTexts>;
15
+ dialogProps?: Record<string, unknown>;
16
+ onClose: () => void;
17
+ /** 导入确认:回传合并后的值与本次新增项 */
18
+ onConfirm: (nextValue: string[], added: string[]) => void;
19
+ }
20
+ /**
21
+ * 批量导入弹窗:
22
+ * 粘贴文本 → 按 代码/中文名/英文名(大小写不敏感)解析 → 分类展示 → 确认导入
23
+ */
24
+ declare const ImportDialog: FC<ImportDialogProps>;
25
+ export default ImportDialog;
@@ -0,0 +1,244 @@
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/components/PntRegionSelect/ImportDialog.tsx
20
+ var ImportDialog_exports = {};
21
+ __export(ImportDialog_exports, {
22
+ default: () => ImportDialog_default
23
+ });
24
+ module.exports = __toCommonJS(ImportDialog_exports);
25
+ var import_react = require("react");
26
+ var import_tdesign_icons_react = require("tdesign-icons-react");
27
+ var import_tdesign_react = require("tdesign-react");
28
+ var import_jsx_runtime = require("react/jsx-runtime");
29
+ var SEPARATOR = /[\r?\n,,;;]+/;
30
+ var ImportDialog = ({
31
+ visible,
32
+ options,
33
+ currentValue,
34
+ texts,
35
+ dialogProps,
36
+ onClose,
37
+ onConfirm
38
+ }) => {
39
+ const [importText, setImportText] = (0, import_react.useState)("");
40
+ const [rows, setRows] = (0, import_react.useState)([]);
41
+ const [isParsed, setIsParsed] = (0, import_react.useState)(false);
42
+ const aliasMap = (0, import_react.useMemo)(() => {
43
+ const map = /* @__PURE__ */ new Map();
44
+ options.filter((item) => !item.disabled).forEach((item) => {
45
+ [item.region_code, item.region_ch, item.region_en].filter(Boolean).map((alias) => String(alias).trim().toLowerCase()).forEach((alias) => {
46
+ if (alias)
47
+ map.set(alias, item.region_code);
48
+ });
49
+ });
50
+ return map;
51
+ }, [options]);
52
+ const { pendingList, existsList, unmatchedList } = (0, import_react.useMemo)(() => {
53
+ const selectedSet = new Set(currentValue);
54
+ const pending = [];
55
+ const exists = [];
56
+ const unmatched = [];
57
+ const seenPending = /* @__PURE__ */ new Set();
58
+ const seenExists = /* @__PURE__ */ new Set();
59
+ const seenUnmatched = /* @__PURE__ */ new Set();
60
+ rows.forEach((row) => {
61
+ if (row.status === "unmatched") {
62
+ if (!seenUnmatched.has(row.region)) {
63
+ seenUnmatched.add(row.region);
64
+ unmatched.push(row.region);
65
+ }
66
+ return;
67
+ }
68
+ const code = aliasMap.get(row.region.toLowerCase());
69
+ if (!code)
70
+ return;
71
+ if (row.status === "exists" || selectedSet.has(code)) {
72
+ if (!seenExists.has(code)) {
73
+ seenExists.add(code);
74
+ exists.push(code);
75
+ }
76
+ } else if (!seenPending.has(code)) {
77
+ seenPending.add(code);
78
+ pending.push(code);
79
+ }
80
+ });
81
+ return {
82
+ pendingList: pending,
83
+ existsList: exists,
84
+ unmatchedList: unmatched
85
+ };
86
+ }, [rows, aliasMap, currentValue]);
87
+ const reset = () => {
88
+ setImportText("");
89
+ setRows([]);
90
+ setIsParsed(false);
91
+ };
92
+ const handleClose = () => {
93
+ reset();
94
+ onClose();
95
+ };
96
+ const parse = () => {
97
+ const uniqueInputs = Array.from(
98
+ new Set(
99
+ importText.split(SEPARATOR).map((item) => item.trim()).filter(Boolean)
100
+ )
101
+ );
102
+ const selectedSet = new Set(currentValue);
103
+ const nextRows = uniqueInputs.map((input) => {
104
+ const code = aliasMap.get(input.toLowerCase());
105
+ if (!code)
106
+ return { region: input, status: "unmatched" };
107
+ return {
108
+ region: input,
109
+ status: selectedSet.has(code) ? "exists" : "pending"
110
+ };
111
+ });
112
+ const priority = {
113
+ unmatched: 0,
114
+ pending: 1,
115
+ exists: 2
116
+ };
117
+ nextRows.sort((a, b) => priority[a.status] - priority[b.status]);
118
+ setRows(nextRows);
119
+ setIsParsed(true);
120
+ };
121
+ const copyUnmatched = async () => {
122
+ if (!unmatchedList.length)
123
+ return;
124
+ const text = unmatchedList.join("\n");
125
+ try {
126
+ if (navigator.clipboard && window.isSecureContext) {
127
+ await navigator.clipboard.writeText(text);
128
+ } else {
129
+ const textArea = document.createElement("textarea");
130
+ textArea.value = text;
131
+ document.body.appendChild(textArea);
132
+ textArea.focus();
133
+ textArea.select();
134
+ document.execCommand("copy");
135
+ textArea.remove();
136
+ }
137
+ import_tdesign_react.MessagePlugin.success(texts.copySuccess);
138
+ } catch {
139
+ }
140
+ };
141
+ const handleConfirm = () => {
142
+ if (!isParsed) {
143
+ parse();
144
+ return;
145
+ }
146
+ const nextSet = new Set(currentValue);
147
+ pendingList.forEach((code) => nextSet.add(code));
148
+ const nextValue = Array.from(nextSet);
149
+ onConfirm(nextValue, pendingList);
150
+ handleClose();
151
+ };
152
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
153
+ import_tdesign_react.Dialog,
154
+ {
155
+ visible,
156
+ header: texts.importTitle,
157
+ width: 520,
158
+ closeOnOverlayClick: false,
159
+ confirmBtn: isParsed ? texts.importConfirmBtn : texts.parseBtn,
160
+ cancelBtn: texts.cancelBtn,
161
+ onConfirm: handleConfirm,
162
+ onClose: handleClose,
163
+ ...dialogProps,
164
+ children: [
165
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-region-import__tip", children: texts.importTip }),
166
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
167
+ import_tdesign_react.Textarea,
168
+ {
169
+ value: importText,
170
+ onChange: (next) => {
171
+ setImportText(String(next != null ? next : ""));
172
+ if (isParsed) {
173
+ setRows([]);
174
+ setIsParsed(false);
175
+ }
176
+ },
177
+ rows: 3,
178
+ placeholder: texts.importPlaceholder
179
+ }
180
+ ),
181
+ isParsed ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-region-import__result", children: [
182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-region-import__summary", children: [
183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "pnt-region-import__summary-item pnt-region-import__summary-item--unmatched", children: [
184
+ texts.importUnmatched,
185
+ ":",
186
+ unmatchedList.length
187
+ ] }),
188
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tdesign_react.Tooltip, { content: texts.copyUnmatchedTip, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
189
+ import_tdesign_react.Button,
190
+ {
191
+ theme: "primary",
192
+ variant: "text",
193
+ size: "small",
194
+ className: "pnt-region-import__copy-btn",
195
+ disabled: !unmatchedList.length,
196
+ onClick: copyUnmatched,
197
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tdesign_icons_react.FileCopyIcon, { strokeWidth: 2 })
198
+ }
199
+ ) }),
200
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pnt-region-import__summary-dot", children: "·" }),
201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "pnt-region-import__summary-item pnt-region-import__summary-item--pending", children: [
202
+ texts.importPending,
203
+ ":",
204
+ pendingList.length
205
+ ] }),
206
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pnt-region-import__summary-dot", children: "·" }),
207
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "pnt-region-import__summary-item pnt-region-import__summary-item--exists", children: [
208
+ texts.importExists,
209
+ ":",
210
+ existsList.length
211
+ ] })
212
+ ] }),
213
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-region-import__table", children: [
214
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "pnt-region-import__table-head", children: [
215
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: texts.importRegionColumn }),
216
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: texts.importStatusColumn })
217
+ ] }),
218
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-region-import__table-body", children: rows.length ? rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
219
+ "div",
220
+ {
221
+ className: "pnt-region-import__table-row",
222
+ children: [
223
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "pnt-region-import__region", children: row.region }),
224
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
225
+ "span",
226
+ {
227
+ className: `pnt-region-import__status pnt-region-import__status--${row.status}`,
228
+ children: [
229
+ row.status === "unmatched" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tdesign_icons_react.CloseIcon, { strokeWidth: 2 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tdesign_icons_react.CheckIcon, { strokeWidth: 2 }),
230
+ row.status === "pending" ? texts.importPending : row.status === "exists" ? texts.importExists : texts.importUnmatched
231
+ ]
232
+ }
233
+ )
234
+ ]
235
+ },
236
+ row.region
237
+ )) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pnt-region-import__empty", children: "-" }) })
238
+ ] })
239
+ ] }) : null
240
+ ]
241
+ }
242
+ );
243
+ };
244
+ var ImportDialog_default = ImportDialog;
@@ -0,0 +1,34 @@
1
+ import { FC } from 'react';
2
+ import './index.scss';
3
+ import type { PntRegionSelectProps } from './types';
4
+ /**
5
+ * 地区选择器组件
6
+ * @description 统一的国家/地区选择组件,支持单选/多选、批量选择、批量导入、自定义区域、合规区域
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * // 基础多选(内部拉取默认区域列表)
11
+ * <PntRegionSelect
12
+ * value={regions}
13
+ * onChange={(v) => setRegions(v)}
14
+ * />
15
+ *
16
+ * // 业务最终生效区域
17
+ * <PntRegionSelect
18
+ * dataSource="business"
19
+ * gameId={gameId}
20
+ * value={regions}
21
+ * onChange={(v) => setRegions(v)}
22
+ * />
23
+ *
24
+ * // 外部受控选项 + 批量导入
25
+ * <PntRegionSelect
26
+ * options={regionList}
27
+ * importable
28
+ * value={regions}
29
+ * onChange={(v) => setRegions(v)}
30
+ * />
31
+ * ```
32
+ */
33
+ declare const PntRegionSelect: FC<PntRegionSelectProps>;
34
+ export default PntRegionSelect;