@pnt-team/pnt-component-frontend 0.0.111 → 0.0.113
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 +29 -30
- package/lib/cjs/api/index.d.ts +14 -1
- package/lib/cjs/api/index.js +6 -0
- package/lib/cjs/api/types/index.d.ts +47 -0
- package/lib/cjs/components/PntRegionSelect/ImportDialog.d.ts +25 -0
- package/lib/cjs/components/PntRegionSelect/ImportDialog.js +244 -0
- package/lib/cjs/components/PntRegionSelect/index.d.ts +43 -0
- package/lib/cjs/components/PntRegionSelect/index.js +402 -0
- package/lib/cjs/components/PntRegionSelect/index.scss +166 -0
- package/lib/cjs/components/PntRegionSelect/types.d.ts +158 -0
- package/lib/cjs/components/PntRegionSelect/types.js +51 -0
- package/lib/cjs/components/PntSecureBox/index.scss +3 -0
- package/lib/cjs/index.d.ts +3 -1
- package/lib/cjs/index.js +3 -0
- package/lib/esm/api/index.d.ts +14 -1
- package/lib/esm/api/index.js +16 -1
- package/lib/esm/api/types/index.d.ts +47 -0
- package/lib/esm/components/PntRegionSelect/ImportDialog.d.ts +25 -0
- package/lib/esm/components/PntRegionSelect/ImportDialog.js +241 -0
- package/lib/esm/components/PntRegionSelect/index.d.ts +43 -0
- package/lib/esm/components/PntRegionSelect/index.js +388 -0
- package/lib/esm/components/PntRegionSelect/index.scss +166 -0
- package/lib/esm/components/PntRegionSelect/types.d.ts +158 -0
- package/lib/esm/components/PntRegionSelect/types.js +51 -0
- package/lib/esm/components/PntSecureBox/index.scss +3 -0
- package/lib/esm/index.d.ts +3 -1
- package/lib/esm/index.js +1 -0
- package/lib/umd/pnt-component-frontend.min.css +1 -1
- package/lib/umd/pnt-component-frontend.min.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
/**
|
|
3
|
+
* 地区选项数据结构(统一格式)
|
|
4
|
+
*/
|
|
5
|
+
export interface RegionOption {
|
|
6
|
+
/** 地区代码(唯一标识,如 'CN'、'HK') */
|
|
7
|
+
region_code: string;
|
|
8
|
+
/** 中文名称 */
|
|
9
|
+
region_ch: string;
|
|
10
|
+
/** 英文名称 */
|
|
11
|
+
region_en: string;
|
|
12
|
+
/** ISO 3166-1 alpha-2 代码 */
|
|
13
|
+
region_a2_code?: string;
|
|
14
|
+
/** ISO 3166-1 alpha-3 代码 */
|
|
15
|
+
region_a3_code?: string;
|
|
16
|
+
/** 是否禁用(合规区域 creatable=false 时使用) */
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
/** 禁用原因提示 */
|
|
19
|
+
disabledReason?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 数据源类型
|
|
23
|
+
* - default: 全部国家区域(不带任何参数,返回全部 251 个)/api/v5/business/region/query
|
|
24
|
+
* - business: 业务最终生效的区域(必须传 gameId)/api/v5/business/region/query
|
|
25
|
+
* - custom: 自定义合规区域 /v4/business/game_info/get-custom-region
|
|
26
|
+
* - compliance: 合规区域(从业务配置接口获取 compliance_zone)
|
|
27
|
+
*/
|
|
28
|
+
export type RegionDataSource = 'default' | 'business' | 'custom' | 'compliance';
|
|
29
|
+
/**
|
|
30
|
+
* 标签格式
|
|
31
|
+
* - name: 中文名
|
|
32
|
+
* - name_en: 英文名
|
|
33
|
+
* - name_en_name: 英文名(中文名)
|
|
34
|
+
* - code: 仅代码
|
|
35
|
+
*/
|
|
36
|
+
export type RegionLabelFormat = 'name' | 'name_en' | 'name_en_name' | 'code';
|
|
37
|
+
/**
|
|
38
|
+
* 组件文案集合
|
|
39
|
+
*/
|
|
40
|
+
export interface PntRegionSelectTexts {
|
|
41
|
+
/** 占位提示 */
|
|
42
|
+
placeholder?: string;
|
|
43
|
+
/** 全选按钮 */
|
|
44
|
+
selectAll?: string;
|
|
45
|
+
/** 反选按钮 */
|
|
46
|
+
invertSelection?: string;
|
|
47
|
+
/** 清空按钮 */
|
|
48
|
+
clearAll?: string;
|
|
49
|
+
/** 已选数量提示({count} 为占位符) */
|
|
50
|
+
selectedCount?: string;
|
|
51
|
+
/** 搜索无结果提示 */
|
|
52
|
+
noResult?: string;
|
|
53
|
+
/** 加载中提示 */
|
|
54
|
+
loading?: string;
|
|
55
|
+
/** 批量导入按钮 */
|
|
56
|
+
importBtn?: string;
|
|
57
|
+
/** 批量导入弹窗标题 */
|
|
58
|
+
importTitle?: string;
|
|
59
|
+
/** 批量导入弹窗说明 */
|
|
60
|
+
importTip?: string;
|
|
61
|
+
/** 批量导入输入框占位 */
|
|
62
|
+
importPlaceholder?: string;
|
|
63
|
+
/** 解析按钮 */
|
|
64
|
+
parseBtn?: string;
|
|
65
|
+
/** 导入确认按钮 */
|
|
66
|
+
importConfirmBtn?: string;
|
|
67
|
+
/** 取消按钮 */
|
|
68
|
+
cancelBtn?: string;
|
|
69
|
+
/** 解析结果:未匹配 */
|
|
70
|
+
importUnmatched?: string;
|
|
71
|
+
/** 解析结果:待导入 */
|
|
72
|
+
importPending?: string;
|
|
73
|
+
/** 解析结果:已存在 */
|
|
74
|
+
importExists?: string;
|
|
75
|
+
/** 复制未匹配项提示 */
|
|
76
|
+
copyUnmatchedTip?: string;
|
|
77
|
+
/** 复制成功提示 */
|
|
78
|
+
copySuccess?: string;
|
|
79
|
+
/** 解析结果表格:地区列 */
|
|
80
|
+
importRegionColumn?: string;
|
|
81
|
+
/** 解析结果表格:状态列 */
|
|
82
|
+
importStatusColumn?: string;
|
|
83
|
+
}
|
|
84
|
+
export declare const DEFAULT_TEXTS: Required<PntRegionSelectTexts>;
|
|
85
|
+
/**
|
|
86
|
+
* 地区选择器组件 Props
|
|
87
|
+
*/
|
|
88
|
+
export interface PntRegionSelectProps {
|
|
89
|
+
/**
|
|
90
|
+
* 当前选中值:多选为地区代码数组,单选为单个地区代码
|
|
91
|
+
* (兼容 TDesign FormItem 注入的单值/多值)
|
|
92
|
+
*/
|
|
93
|
+
value?: string | string[];
|
|
94
|
+
/**
|
|
95
|
+
* 值变化回调:多选时回传 (string[], RegionOption[]),
|
|
96
|
+
* 单选时回传 (string, RegionOption)
|
|
97
|
+
*/
|
|
98
|
+
onChange?: (value: string[] | string, options: RegionOption[] | RegionOption) => void;
|
|
99
|
+
/** 是否多选 @default true */
|
|
100
|
+
multiple?: boolean;
|
|
101
|
+
/** 数据源类型 @default 'default' */
|
|
102
|
+
dataSource?: RegionDataSource;
|
|
103
|
+
/** 业务ID(dataSource='custom' | 'business' 时必传) */
|
|
104
|
+
gameId?: string | number;
|
|
105
|
+
/** 环境(dataSource='custom' 时必传) @default 0 */
|
|
106
|
+
env?: number;
|
|
107
|
+
/**
|
|
108
|
+
* 外部受控选项列表(传入后跳过内部请求,直接使用该数据;
|
|
109
|
+
* 适用于选项由父组件获取/过滤的场景)
|
|
110
|
+
*/
|
|
111
|
+
options?: RegionOption[];
|
|
112
|
+
/** 是否禁用 @default false */
|
|
113
|
+
disabled?: boolean;
|
|
114
|
+
/** 占位文案 */
|
|
115
|
+
placeholder?: string;
|
|
116
|
+
/** 最大可选数量(多选时生效) */
|
|
117
|
+
max?: number;
|
|
118
|
+
/** 是否可搜索 @default true */
|
|
119
|
+
filterable?: boolean;
|
|
120
|
+
/** 标签格式 @default 'name' */
|
|
121
|
+
labelFormat?: RegionLabelFormat;
|
|
122
|
+
/** 自定义文案 */
|
|
123
|
+
texts?: PntRegionSelectTexts;
|
|
124
|
+
/** 数据加载完成回调 */
|
|
125
|
+
onLoaded?: (options: RegionOption[]) => void;
|
|
126
|
+
/** 自定义选项禁用逻辑 */
|
|
127
|
+
optionDisabled?: (item: RegionOption) => boolean;
|
|
128
|
+
/** 自定义选项 tooltip */
|
|
129
|
+
optionTooltip?: (item: RegionOption) => string;
|
|
130
|
+
/**
|
|
131
|
+
* 是否启用批量导入(仅多选时生效):
|
|
132
|
+
* 输入框右侧显示导入按钮,支持粘贴文本按 代码/中文名/英文名 解析匹配
|
|
133
|
+
* @default false
|
|
134
|
+
*/
|
|
135
|
+
importable?: boolean;
|
|
136
|
+
/** 是否在工具栏显示反选按钮(仅多选时生效) @default false */
|
|
137
|
+
invertible?: boolean;
|
|
138
|
+
/** 最小折叠数量(多选时透传 TDesign Select) */
|
|
139
|
+
minCollapsedNum?: number;
|
|
140
|
+
/** 折叠项自定义渲染(透传 TDesign Select) */
|
|
141
|
+
collapsedItems?: (props: {
|
|
142
|
+
value: unknown[];
|
|
143
|
+
collapsedSelectedItems: unknown[];
|
|
144
|
+
count: number;
|
|
145
|
+
}) => React.ReactNode;
|
|
146
|
+
/** 失焦回调(透传 TDesign Select,常用于表单校验触发) */
|
|
147
|
+
onBlur?: (context: {
|
|
148
|
+
value: unknown;
|
|
149
|
+
}) => void;
|
|
150
|
+
/** 批量导入弹窗属性透传(如 attach 挂载容器) */
|
|
151
|
+
importDialogProps?: Record<string, unknown>;
|
|
152
|
+
/** 自定义类名 */
|
|
153
|
+
className?: string;
|
|
154
|
+
/** 自定义样式 */
|
|
155
|
+
style?: React.CSSProperties;
|
|
156
|
+
/** 弹层属性透传 */
|
|
157
|
+
popupProps?: Record<string, unknown>;
|
|
158
|
+
}
|
|
@@ -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
|
+
});
|
package/lib/cjs/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
export { PntProvider, usePntConfig, type PntConfig, type
|
|
1
|
+
export { PntProvider, usePntConfig, type PntConfig, type PntHighSecurityConfig, type PntHttpLike, 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';
|
package/lib/cjs/index.js
CHANGED
|
@@ -33,6 +33,7 @@ __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,
|
|
37
38
|
isSecurityData: () => import_utils.isSecurityData,
|
|
38
39
|
useHighSecurity: () => import_useHighSecurity.useHighSecurity,
|
|
@@ -42,6 +43,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
42
43
|
var import_config = require("./config");
|
|
43
44
|
var import_PntImage = __toESM(require("./components/PntImage"));
|
|
44
45
|
var import_PntBusinessSelect = __toESM(require("./components/PntBusinessSelect"));
|
|
46
|
+
var import_PntRegionSelect = __toESM(require("./components/PntRegionSelect"));
|
|
45
47
|
var import_PntSecureBox = __toESM(require("./components/PntSecureBox"));
|
|
46
48
|
var import_useHighSecurity = require("./hooks/useHighSecurity");
|
|
47
49
|
var import_utils = require("./utils");
|
|
@@ -52,6 +54,7 @@ var import_PntGeneralLayout = __toESM(require("./components/PntGeneralLayout"));
|
|
|
52
54
|
PntGeneralLayout,
|
|
53
55
|
PntImage,
|
|
54
56
|
PntProvider,
|
|
57
|
+
PntRegionSelect,
|
|
55
58
|
PntSecureBox,
|
|
56
59
|
isSecurityData,
|
|
57
60
|
useHighSecurity,
|
package/lib/esm/api/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { commonEesType, PntHttpLike } from "../config";
|
|
2
|
-
import { BusinessGameInfoHighSecurityDecryptBody, BusinessGameInfoHighSecurityDecryptResponse, BusinessSelectListBody, BusinessSelectListResponse } from './types/index';
|
|
2
|
+
import { BusinessGameInfoGetCustomRegionBody, BusinessGameInfoGetCustomRegionResponse, BusinessGameInfoHighSecurityDecryptBody, BusinessGameInfoHighSecurityDecryptResponse, BusinessRegionQueryBody, BusinessRegionQueryResponse, BusinessSelectListBody, BusinessSelectListResponse } from './types/index';
|
|
3
3
|
/**
|
|
4
4
|
* 业务下拉选择列表
|
|
5
5
|
* @param http 由 PntProvider 注入的 axios 实例
|
|
@@ -13,3 +13,16 @@ 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
|
+
*/
|
|
22
|
+
export declare const businessRegionQuery: (axios: PntHttpLike, data?: BusinessRegionQueryBody) => Promise<commonEesType<BusinessRegionQueryResponse>>;
|
|
23
|
+
/**
|
|
24
|
+
* 自定义合规区域列表
|
|
25
|
+
* @param axios 由 PntProvider 注入的 axios 实例
|
|
26
|
+
* @param data 请求参数(game_id + env)
|
|
27
|
+
*/
|
|
28
|
+
export declare const businessGameInfoGetCustomRegion: (axios: PntHttpLike, data: BusinessGameInfoGetCustomRegionBody) => Promise<commonEesType<BusinessGameInfoGetCustomRegionResponse>>;
|
package/lib/esm/api/index.js
CHANGED
|
@@ -11,4 +11,19 @@ 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
|
+
*/
|
|
22
|
+
export const businessRegionQuery = (axios, data) => axios.post('/api/v5/business/region/query?audience=/laas-pnt', data);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 自定义合规区域列表
|
|
26
|
+
* @param axios 由 PntProvider 注入的 axios 实例
|
|
27
|
+
* @param data 请求参数(game_id + env)
|
|
28
|
+
*/
|
|
29
|
+
export const businessGameInfoGetCustomRegion = (axios, data) => axios.post('/v4/business/game_info/get-custom-region?audience=/laas-pnt', data);
|
|
@@ -78,3 +78,50 @@ export interface BusinessGameInfoHighSecurityDecryptBody {
|
|
|
78
78
|
game_id: number;
|
|
79
79
|
}
|
|
80
80
|
export type BusinessGameInfoHighSecurityDecryptResponse = Record<string, unknown>[];
|
|
81
|
+
export interface BusinessRegionQueryBody {
|
|
82
|
+
/** GameID 目标业务 ID;可选,取值必须 > 0。
|
|
83
|
+
* 不传时不带任何参数,返回全部国家区域(251 个);传入时返回该业务最终生效的区域。 */
|
|
84
|
+
game_id?: number;
|
|
85
|
+
/** Search 模糊搜索关键字,按 name_en / name_ch / num_code 匹配;可选,默认空字符串。 */
|
|
86
|
+
search?: string;
|
|
87
|
+
}
|
|
88
|
+
export interface BusinessRegionQueryResponseRegionListItem {
|
|
89
|
+
/** ID 区域主键 ID。 */
|
|
90
|
+
id?: number;
|
|
91
|
+
/** IsEEARegion 是否 EEA 区域。 */
|
|
92
|
+
is_eea_region?: number;
|
|
93
|
+
/** IsSecretRegion 是否特殊合规区域,取值:1=默认区域,2=特殊区域。 */
|
|
94
|
+
is_secret_region?: number;
|
|
95
|
+
/** RegionA2Code 二位字母码。 */
|
|
96
|
+
region_a2_code?: string;
|
|
97
|
+
/** RegionA3Code 三位字母码。 */
|
|
98
|
+
region_a3_code?: string;
|
|
99
|
+
/** RegionCH 中文名称。 */
|
|
100
|
+
region_ch?: string;
|
|
101
|
+
/** RegionCode 数字码,按数据库原始值返回,不做前导零转换。 */
|
|
102
|
+
region_code?: string;
|
|
103
|
+
/** RegionEN 英文名称。 */
|
|
104
|
+
region_en?: string;
|
|
105
|
+
}
|
|
106
|
+
export interface BusinessRegionQueryResponse {
|
|
107
|
+
/** BlackCodes 黑名单数字码列表;base_region_list / business_effective_region_list 返回空数组,business_region_rule_detail 返回真实值。 */
|
|
108
|
+
black_codes?: string[];
|
|
109
|
+
/** RegionList 国家区域列表;base_region_list 返回完整字典(或搜索子集),
|
|
110
|
+
business_effective_region_list 返回业务最终生效结果,business_region_rule_detail 返回完整字典(或搜索子集)。 */
|
|
111
|
+
region_list?: BusinessRegionQueryResponseRegionListItem[];
|
|
112
|
+
/** RuleState 业务规则状态,取值见 RuleState* 常量:0=未配置,1=启用,2=停用;
|
|
113
|
+
仅 business_effective_region_list / business_region_rule_detail 场景返回;base_region_list 场景省略(omitempty)。 */
|
|
114
|
+
rule_state?: number;
|
|
115
|
+
/** WhiteCodes 白名单数字码列表;base_region_list / business_effective_region_list 返回空数组,business_region_rule_detail 返回真实值。 */
|
|
116
|
+
white_codes?: string[];
|
|
117
|
+
}
|
|
118
|
+
export interface BusinessGameInfoGetCustomRegionBody {
|
|
119
|
+
/** GameID 目标业务 ID */
|
|
120
|
+
game_id: number;
|
|
121
|
+
/** Env 环境 */
|
|
122
|
+
env: number;
|
|
123
|
+
}
|
|
124
|
+
export interface BusinessGameInfoGetCustomRegionResponse {
|
|
125
|
+
/** RegionList 自定义合规区域列表 */
|
|
126
|
+
region_list?: BusinessRegionQueryResponseRegionListItem[];
|
|
127
|
+
}
|
|
@@ -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,43 @@
|
|
|
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="custom"
|
|
19
|
+
* gameId={gameId}
|
|
20
|
+
* env={0}
|
|
21
|
+
* value={regions}
|
|
22
|
+
* onChange={(v) => setRegions(v)}
|
|
23
|
+
* />
|
|
24
|
+
*
|
|
25
|
+
* // 业务最终生效区域
|
|
26
|
+
* <PntRegionSelect
|
|
27
|
+
* dataSource="business"
|
|
28
|
+
* gameId={gameId}
|
|
29
|
+
* value={regions}
|
|
30
|
+
* onChange={(v) => setRegions(v)}
|
|
31
|
+
* />
|
|
32
|
+
*
|
|
33
|
+
* // 外部受控选项 + 批量导入
|
|
34
|
+
* <PntRegionSelect
|
|
35
|
+
* options={regionList}
|
|
36
|
+
* importable
|
|
37
|
+
* value={regions}
|
|
38
|
+
* onChange={(v) => setRegions(v)}
|
|
39
|
+
* />
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
declare const PntRegionSelect: FC<PntRegionSelectProps>;
|
|
43
|
+
export default PntRegionSelect;
|