@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
@@ -0,0 +1,51 @@
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/types.ts
20
+ var types_exports = {};
21
+ __export(types_exports, {
22
+ DEFAULT_TEXTS: () => DEFAULT_TEXTS
23
+ });
24
+ module.exports = __toCommonJS(types_exports);
25
+ var DEFAULT_TEXTS = {
26
+ placeholder: "请选择国家/地区",
27
+ selectAll: "全选",
28
+ invertSelection: "反选",
29
+ clearAll: "清空",
30
+ selectedCount: "已选 {count} 项",
31
+ noResult: "未找到匹配的地区",
32
+ loading: "加载中...",
33
+ importBtn: "批量导入",
34
+ importTitle: "批量导入国家/地区",
35
+ importTip: "支持输入地区代码、中文名或英文名,多个地区以换行、逗号或分号分隔",
36
+ importPlaceholder: "请输入地区代码、中文名或英文名",
37
+ parseBtn: "解析",
38
+ importConfirmBtn: "导入",
39
+ cancelBtn: "取消",
40
+ importUnmatched: "未匹配",
41
+ importPending: "待导入",
42
+ importExists: "已存在",
43
+ copyUnmatchedTip: "复制未匹配项",
44
+ copySuccess: "复制成功",
45
+ importRegionColumn: "地区",
46
+ importStatusColumn: "状态"
47
+ };
48
+ // Annotate the CommonJS export names for ESM import in node:
49
+ 0 && (module.exports = {
50
+ DEFAULT_TEXTS
51
+ });
@@ -12,6 +12,9 @@
12
12
  cursor: pointer;
13
13
  font-size: small;
14
14
  }
15
+ .copy-icon {
16
+ right: -50px;
17
+ }
15
18
 
16
19
  .security-value {
17
20
  position: relative;
@@ -37,6 +37,21 @@ export interface PntHighSecurityConfig {
37
37
  /** audience 覆盖(默认 /laas-pnt) */
38
38
  audience?: string;
39
39
  }
40
+ /**
41
+ * 地区查询接口配置(可选,全局覆盖)
42
+ */
43
+ export interface PntRegionQueryConfig {
44
+ /**
45
+ * 业务级地区查询接口 URL,覆盖默认 /api/v5/business/region/query?audience=/laas-pnt
46
+ * @description scope='business'(默认)时使用
47
+ */
48
+ queryUrl?: string;
49
+ /**
50
+ * 平台级地区查询接口 URL,覆盖默认 /api/v5/business/region/plat-query?audience=/laas-pnt
51
+ * @description scope='platform' 时使用
52
+ */
53
+ queryPlatUrl?: string;
54
+ }
40
55
  /**
41
56
  * 组件库运行时配置:由消费方通过 PntProvider 注入
42
57
  */
@@ -45,6 +60,8 @@ export interface PntConfig {
45
60
  http: PntHttpLike;
46
61
  /** 高敏信息解锁接口全局配置(可选) */
47
62
  highSecurity?: PntHighSecurityConfig;
63
+ /** 地区查询接口全局配置(可选) */
64
+ regionQuery?: PntRegionQueryConfig;
48
65
  }
49
66
  /**
50
67
  * 配置 Provider:消费方在应用根入口包裹一次即可
@@ -1,8 +1,12 @@
1
- export { PntProvider, usePntConfig, type PntConfig, type PntHttpLike, type PntHighSecurityConfig, type commonEesType, } from './config';
1
+ export { PntProvider, usePntConfig, type PntConfig, type PntHighSecurityConfig, type PntHttpLike, type PntRegionQueryConfig, type commonEesType, } from './config';
2
2
  export { default as PntImage } from './components/PntImage';
3
3
  export { default as PntBusinessSelect } from './components/PntBusinessSelect';
4
+ export { default as PntRegionSelect } from './components/PntRegionSelect';
5
+ export type { PntRegionSelectProps, PntRegionSelectTexts, RegionDataSource, RegionLabelFormat, RegionOption, } from './components/PntRegionSelect/types';
4
6
  export { default as PntSecureBox } from './components/PntSecureBox';
5
7
  export { useHighSecurity } from './hooks/useHighSecurity';
6
8
  export type { ClearSecurityDataFilter, HighSecurityApiConfig, HighSecurityParamItem, HighSecurityRequestParams, HighSecurityScope, UseHighSecurityOptions, UseHighSecurityResult, } from './hooks/useHighSecurity';
7
9
  export { isSecurityData } from './utils';
10
+ export { businessGameInfoHighSecurityDecrypt, businessRegionQuery, businessSelectList, } from './api';
11
+ export type { BusinessRegionQueryBody, BusinessRegionQueryResponse, BusinessRegionQueryResponseRegionListItem, } from './api/types';
8
12
  export { default as PntGeneralLayout } from './components/PntGeneralLayout';
package/lib/cjs/index.js CHANGED
@@ -33,7 +33,11 @@ __export(src_exports, {
33
33
  PntGeneralLayout: () => import_PntGeneralLayout.default,
34
34
  PntImage: () => import_PntImage.default,
35
35
  PntProvider: () => import_config.PntProvider,
36
+ PntRegionSelect: () => import_PntRegionSelect.default,
36
37
  PntSecureBox: () => import_PntSecureBox.default,
38
+ businessGameInfoHighSecurityDecrypt: () => import_api.businessGameInfoHighSecurityDecrypt,
39
+ businessRegionQuery: () => import_api.businessRegionQuery,
40
+ businessSelectList: () => import_api.businessSelectList,
37
41
  isSecurityData: () => import_utils.isSecurityData,
38
42
  useHighSecurity: () => import_useHighSecurity.useHighSecurity,
39
43
  usePntConfig: () => import_config.usePntConfig
@@ -42,9 +46,11 @@ module.exports = __toCommonJS(src_exports);
42
46
  var import_config = require("./config");
43
47
  var import_PntImage = __toESM(require("./components/PntImage"));
44
48
  var import_PntBusinessSelect = __toESM(require("./components/PntBusinessSelect"));
49
+ var import_PntRegionSelect = __toESM(require("./components/PntRegionSelect"));
45
50
  var import_PntSecureBox = __toESM(require("./components/PntSecureBox"));
46
51
  var import_useHighSecurity = require("./hooks/useHighSecurity");
47
52
  var import_utils = require("./utils");
53
+ var import_api = require("./api");
48
54
  var import_PntGeneralLayout = __toESM(require("./components/PntGeneralLayout"));
49
55
  // Annotate the CommonJS export names for ESM import in node:
50
56
  0 && (module.exports = {
@@ -52,7 +58,11 @@ var import_PntGeneralLayout = __toESM(require("./components/PntGeneralLayout"));
52
58
  PntGeneralLayout,
53
59
  PntImage,
54
60
  PntProvider,
61
+ PntRegionSelect,
55
62
  PntSecureBox,
63
+ businessGameInfoHighSecurityDecrypt,
64
+ businessRegionQuery,
65
+ businessSelectList,
56
66
  isSecurityData,
57
67
  useHighSecurity,
58
68
  usePntConfig
@@ -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>>;
@@ -11,4 +11,13 @@ export const businessSelectList = (axios, data) => axios.post('/business/select/
11
11
  * @param data 请求参数
12
12
  * @param url 自定义接口 URL(可选,覆盖默认值)
13
13
  */
14
- export const businessGameInfoHighSecurityDecrypt = (axios, data, url) => axios.post(url || '/v3/business/game_info/high-security-decrypt?audience=/laas-pnt', data);
14
+ export const businessGameInfoHighSecurityDecrypt = (axios, data, url) => axios.post(url || '/v3/business/game_info/high-security-decrypt?audience=/laas-pnt', data);
15
+
16
+ /**
17
+ * 业务区域查询
18
+ * @param axios 由 PntProvider 注入的 axios 实例
19
+ * @param data 请求参数;不传时不带任何参数,返回全部国家区域(251 个);
20
+ * 传 game_id 时返回该业务最终生效的区域
21
+ * @param url 自定义接口 URL(可选,覆盖默认值)
22
+ */
23
+ export const businessRegionQuery = (axios, data, url) => axios.post(url || '/api/v5/business/region/query?audience=/laas-pnt', data);
@@ -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)
@@ -64,7 +64,6 @@ const PntBusinessSelect = props => {
64
64
  scene = 'default',
65
65
  statuses,
66
66
  businessTypes,
67
- businessAccessMethod,
68
67
  labelFormat = 'name',
69
68
  labelField,
70
69
  optionLabel,
@@ -143,11 +142,10 @@ const PntBusinessSelect = props => {
143
142
  scene,
144
143
  statuses,
145
144
  business_types: businessTypes,
146
- business_access_method: businessAccessMethod,
147
145
  fields
148
146
  }),
149
147
  // eslint-disable-next-line react-hooks/exhaustive-deps
150
- [scene, statusesKey, businessTypesKey, businessAccessMethod, pageSize, fieldsKey, pagination]);
148
+ [scene, statusesKey, businessTypesKey, pageSize, fieldsKey, pagination]);
151
149
 
152
150
  // ---- 获取数据 ----
153
151
  // append=true 为滚动翻页(追加),append=false 为初始化/搜索/重置(替换)
@@ -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,241 @@
1
+ import { useMemo, useState } from 'react';
2
+ import { CheckIcon, CloseIcon, FileCopyIcon } from 'tdesign-icons-react';
3
+ import { Button, Dialog, MessagePlugin, Textarea, Tooltip } from 'tdesign-react';
4
+
5
+ /** 解析后单行状态 */
6
+ import { jsx as _jsx } from "react/jsx-runtime";
7
+ import { jsxs as _jsxs } from "react/jsx-runtime";
8
+ /** 分隔符:换行/逗号/分号(中英文) */
9
+ const SEPARATOR = /[\r?\n,,;;]+/;
10
+
11
+ /**
12
+ * 批量导入弹窗:
13
+ * 粘贴文本 → 按 代码/中文名/英文名(大小写不敏感)解析 → 分类展示 → 确认导入
14
+ */
15
+ const ImportDialog = ({
16
+ visible,
17
+ options,
18
+ currentValue,
19
+ texts,
20
+ dialogProps,
21
+ onClose,
22
+ onConfirm
23
+ }) => {
24
+ const [importText, setImportText] = useState('');
25
+ const [rows, setRows] = useState([]);
26
+ const [isParsed, setIsParsed] = useState(false);
27
+
28
+ // ---- 别名映射:代码/中文名/英文名(小写) -> region_code ----
29
+ const aliasMap = useMemo(() => {
30
+ const map = new Map();
31
+ options.filter(item => !item.disabled).forEach(item => {
32
+ [item.region_code, item.region_ch, item.region_en].filter(Boolean).map(alias => String(alias).trim().toLowerCase()).forEach(alias => {
33
+ if (alias) map.set(alias, item.region_code);
34
+ });
35
+ });
36
+ return map;
37
+ }, [options]);
38
+
39
+ // ---- 派生分类列表 ----
40
+ const {
41
+ pendingList,
42
+ existsList,
43
+ unmatchedList
44
+ } = useMemo(() => {
45
+ const selectedSet = new Set(currentValue);
46
+ const pending = [];
47
+ const exists = [];
48
+ const unmatched = [];
49
+ const seenPending = new Set();
50
+ const seenExists = new Set();
51
+ const seenUnmatched = new Set();
52
+ rows.forEach(row => {
53
+ if (row.status === 'unmatched') {
54
+ if (!seenUnmatched.has(row.region)) {
55
+ seenUnmatched.add(row.region);
56
+ unmatched.push(row.region);
57
+ }
58
+ return;
59
+ }
60
+ const code = aliasMap.get(row.region.toLowerCase());
61
+ if (!code) return;
62
+ if (row.status === 'exists' || selectedSet.has(code)) {
63
+ if (!seenExists.has(code)) {
64
+ seenExists.add(code);
65
+ exists.push(code);
66
+ }
67
+ } else if (!seenPending.has(code)) {
68
+ seenPending.add(code);
69
+ pending.push(code);
70
+ }
71
+ });
72
+ return {
73
+ pendingList: pending,
74
+ existsList: exists,
75
+ unmatchedList: unmatched
76
+ };
77
+ }, [rows, aliasMap, currentValue]);
78
+
79
+ // ---- 重置 ----
80
+ const reset = () => {
81
+ setImportText('');
82
+ setRows([]);
83
+ setIsParsed(false);
84
+ };
85
+ const handleClose = () => {
86
+ reset();
87
+ onClose();
88
+ };
89
+
90
+ // ---- 解析 ----
91
+ const parse = () => {
92
+ const uniqueInputs = Array.from(new Set(importText.split(SEPARATOR).map(item => item.trim()).filter(Boolean)));
93
+ const selectedSet = new Set(currentValue);
94
+ const nextRows = uniqueInputs.map(input => {
95
+ const code = aliasMap.get(input.toLowerCase());
96
+ if (!code) return {
97
+ region: input,
98
+ status: 'unmatched'
99
+ };
100
+ return {
101
+ region: input,
102
+ status: selectedSet.has(code) ? 'exists' : 'pending'
103
+ };
104
+ });
105
+
106
+ // 排序:未匹配 > 待导入 > 已存在,同级保持输入顺序
107
+ const priority = {
108
+ unmatched: 0,
109
+ pending: 1,
110
+ exists: 2
111
+ };
112
+ nextRows.sort((a, b) => priority[a.status] - priority[b.status]);
113
+ setRows(nextRows);
114
+ setIsParsed(true);
115
+ };
116
+
117
+ // ---- 复制未匹配项 ----
118
+ const copyUnmatched = async () => {
119
+ if (!unmatchedList.length) return;
120
+ const text = unmatchedList.join('\n');
121
+ try {
122
+ if (navigator.clipboard && window.isSecureContext) {
123
+ await navigator.clipboard.writeText(text);
124
+ } else {
125
+ const textArea = document.createElement('textarea');
126
+ textArea.value = text;
127
+ document.body.appendChild(textArea);
128
+ textArea.focus();
129
+ textArea.select();
130
+ document.execCommand('copy');
131
+ textArea.remove();
132
+ }
133
+ MessagePlugin.success(texts.copySuccess);
134
+ } catch {
135
+ // 复制失败静默
136
+ }
137
+ };
138
+
139
+ // ---- 确认 ----
140
+ const handleConfirm = () => {
141
+ if (!isParsed) {
142
+ parse();
143
+ return;
144
+ }
145
+ const nextSet = new Set(currentValue);
146
+ pendingList.forEach(code => nextSet.add(code));
147
+ const nextValue = Array.from(nextSet);
148
+ onConfirm(nextValue, pendingList);
149
+ handleClose();
150
+ };
151
+ return /*#__PURE__*/_jsxs(Dialog, {
152
+ visible: visible,
153
+ header: texts.importTitle,
154
+ width: 520,
155
+ closeOnOverlayClick: false,
156
+ confirmBtn: isParsed ? texts.importConfirmBtn : texts.parseBtn,
157
+ cancelBtn: texts.cancelBtn,
158
+ onConfirm: handleConfirm,
159
+ onClose: handleClose,
160
+ ...dialogProps,
161
+ children: [/*#__PURE__*/_jsx("div", {
162
+ className: "pnt-region-import__tip",
163
+ children: texts.importTip
164
+ }), /*#__PURE__*/_jsx(Textarea, {
165
+ value: importText,
166
+ onChange: next => {
167
+ setImportText(String(next ?? ''));
168
+ if (isParsed) {
169
+ setRows([]);
170
+ setIsParsed(false);
171
+ }
172
+ },
173
+ rows: 3,
174
+ placeholder: texts.importPlaceholder
175
+ }), isParsed ? /*#__PURE__*/_jsxs("div", {
176
+ className: "pnt-region-import__result",
177
+ children: [/*#__PURE__*/_jsxs("div", {
178
+ className: "pnt-region-import__summary",
179
+ children: [/*#__PURE__*/_jsxs("span", {
180
+ className: "pnt-region-import__summary-item pnt-region-import__summary-item--unmatched",
181
+ children: [texts.importUnmatched, "\uFF1A", unmatchedList.length]
182
+ }), /*#__PURE__*/_jsx(Tooltip, {
183
+ content: texts.copyUnmatchedTip,
184
+ children: /*#__PURE__*/_jsx(Button, {
185
+ theme: "primary",
186
+ variant: "text",
187
+ size: "small",
188
+ className: "pnt-region-import__copy-btn",
189
+ disabled: !unmatchedList.length,
190
+ onClick: copyUnmatched,
191
+ children: /*#__PURE__*/_jsx(FileCopyIcon, {
192
+ strokeWidth: 2
193
+ })
194
+ })
195
+ }), /*#__PURE__*/_jsx("span", {
196
+ className: "pnt-region-import__summary-dot",
197
+ children: "\xB7"
198
+ }), /*#__PURE__*/_jsxs("span", {
199
+ className: "pnt-region-import__summary-item pnt-region-import__summary-item--pending",
200
+ children: [texts.importPending, "\uFF1A", pendingList.length]
201
+ }), /*#__PURE__*/_jsx("span", {
202
+ className: "pnt-region-import__summary-dot",
203
+ children: "\xB7"
204
+ }), /*#__PURE__*/_jsxs("span", {
205
+ className: "pnt-region-import__summary-item pnt-region-import__summary-item--exists",
206
+ children: [texts.importExists, "\uFF1A", existsList.length]
207
+ })]
208
+ }), /*#__PURE__*/_jsxs("div", {
209
+ className: "pnt-region-import__table",
210
+ children: [/*#__PURE__*/_jsxs("div", {
211
+ className: "pnt-region-import__table-head",
212
+ children: [/*#__PURE__*/_jsx("span", {
213
+ children: texts.importRegionColumn
214
+ }), /*#__PURE__*/_jsx("span", {
215
+ children: texts.importStatusColumn
216
+ })]
217
+ }), /*#__PURE__*/_jsx("div", {
218
+ className: "pnt-region-import__table-body",
219
+ children: rows.length ? rows.map(row => /*#__PURE__*/_jsxs("div", {
220
+ className: "pnt-region-import__table-row",
221
+ children: [/*#__PURE__*/_jsx("span", {
222
+ className: "pnt-region-import__region",
223
+ children: row.region
224
+ }), /*#__PURE__*/_jsxs("span", {
225
+ className: `pnt-region-import__status pnt-region-import__status--${row.status}`,
226
+ children: [row.status === 'unmatched' ? /*#__PURE__*/_jsx(CloseIcon, {
227
+ strokeWidth: 2
228
+ }) : /*#__PURE__*/_jsx(CheckIcon, {
229
+ strokeWidth: 2
230
+ }), row.status === 'pending' ? texts.importPending : row.status === 'exists' ? texts.importExists : texts.importUnmatched]
231
+ })]
232
+ }, row.region)) : /*#__PURE__*/_jsx("div", {
233
+ className: "pnt-region-import__empty",
234
+ children: "-"
235
+ })
236
+ })]
237
+ })]
238
+ }) : null]
239
+ });
240
+ };
241
+ export 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;