@jctrans-materials/shared 1.0.41-beta.0 → 1.0.41-beta.10

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
@@ -1,236 +1,631 @@
1
- # 地理位置搜索 SDK
2
-
3
- SDK 提供了一套统一的地理位置(国家、地区、城市、港口、机场、码头等)搜索与查询能力,支持 axios/fetch 双请求适配器、多语言适配、动态配置管理,可快速集成到各类前端项目中。
4
-
5
- ## 特性
6
-
7
- - 🗺️ 覆盖多类型地理实体:国家/地区、省份、城市、港口、机场、码头等
8
- - 🔌 双请求适配器:支持 Axios / Fetch 两种请求方式
9
- - 🌐 多语言适配:自动识别中英文环境,生成对应展示文本
10
- - ⚙️ 动态配置:支持运行时修改请求地址,适配多环境(开发/测试/生产)
11
- - 📦 类型完备:基于 TypeScript 开发,提供完整的类型定义
12
- - 🚀 事件总线:内置全局模态框事件通信能力
1
+ # @jctrans-materials/shared
2
+
3
+ JCTrans 前端共享工具库,为 JCTrans 系列项目提供认证、请求、加密、地理位置搜索、GrowingIO 埋点等基础能力。
4
+
5
+ ## 功能模块
6
+
7
+ | 模块 | 路径 | 说明 |
8
+ |------|------|------|
9
+ | 配置管理 | `config/` | 运行时动态配置(API 地址、AppId 等) |
10
+ | 地理搜索 V1 | `api/baseSearch` | 旧接口搜索(findPageList),使用 appointSearch 结构 |
11
+ | 地理搜索 V2 | `api/searchV2` | 新接口搜索(getLocationOptions),支持 Carrier/Line |
12
+ | 统一类型 | `api/searchTypes` | 搜索模块共享类型定义 |
13
+ | 认证 API | `api/auth` | 登录、注册、第三方登录、密码找回、Session 管理 |
14
+ | 业务 API | `api/common` + `api/applyData` | 验证码、字典、IP 查询、数据上报 |
15
+ | 反查回显 | `api/reverseLookup` | 按英文展示名反查实体详情 |
16
+ | DW-SDK | `dw-sdk/` | 数据仓库 GraphQL SDK 统一入口 |
17
+ | 请求封装 | `utils/request` | Axios/Fetch 双适配器 + 拦截器 |
18
+ | Token 管理 | `utils/storage` | Cookie 凭证存取(Access Token / Refresh Token) |
19
+ | 加密 | `utils/crypto` | AES 加解密(ECB,与后端对齐) |
20
+ | GrowingIO | `utils/gio` | GIO SDK 封装 + v-gio-track 指令支持 |
21
+ | 域名工具 | `utils/domain` | IP 判断、一级域名提取 |
22
+ | AppId/ProjectId | `utils/app` | 跨环境 AppId 和 ProjectId 获取 |
23
+ | 语言 | `utils/lang` | 当前语言获取 |
24
+ | Header 工具 | `utils/header` | HTTP Header 安全处理 |
25
+ | Modal 事件 | `utils/modal` | 全局模态框事件总线 |
26
+ | ClientId | `utils/clientId` | 客户端唯一标识生成与持久化 |
13
27
 
14
28
  ## 安装
15
29
 
16
- ### npm
17
-
18
30
  ```bash
19
- npm install @jctrans-materials/shared --save
31
+ pnpm add @jctrans-materials/shared
20
32
  ```
21
33
 
22
- ### yarn
34
+ **Peer Dependencies:**
23
35
 
24
- ```bash
25
- yarn add @jctrans-materials/shared
36
+ ```json
37
+ {
38
+ "axios": "1.13.2",
39
+ "gio-webjs-sdk": "^4.0.0"
40
+ }
26
41
  ```
27
42
 
28
- ### pnpm
29
-
30
- ```bash
31
- pnpm add @jctrans-materials/shared
32
- ```
43
+ `axios` 为必须依赖,`gio-webjs-sdk` 为可选依赖(仅使用 GIO 埋点时需要)。
33
44
 
34
45
  ## 快速开始
35
46
 
36
- ### 1. 初始化配置(可选)
47
+ ### 1. 初始化配置
37
48
 
38
- 默认使用测试环境地址,可在项目入口处初始化自定义配置:
49
+ 在项目入口调用 `initSharedConfig`,配置 API 域名和路径:
39
50
 
40
51
  ```typescript
41
- import { initSharedConfig } from "@jctrans-materials/shared";
52
+ import { initSharedConfig, initCommonKeys } from "@jctrans-materials/shared";
42
53
 
43
- // 初始化自定义配置
44
54
  initSharedConfig({
45
- prefixPath: "https://api-production.jctrans.com", // 生产环境域名
46
- searchPath: "/system/dms/fr/aggr/getLocationOptions", // 接口路径
55
+ prefixPath: "https://api-production.jctrans.com",
56
+ searchPath: "/system/dms/fr/aggr/getLocationOptions",
57
+ oldSearchPath: "/system/dms/fr/aggr/findPageList",
58
+ carrierPath: "/system/dms/carrier/findAllList",
59
+ linePath: "/system/dms/fr/line/findAllList",
60
+ allLinePath: "/system/dms/fr/line/findAllListByCountry",
61
+ appId: "YOUR_APP_ID",
62
+ commonKeys: {
63
+ TokenKey: "CUSTOM-TOKEN-KEY", // 可选:自定义 Cookie key
64
+ },
47
65
  });
48
66
  ```
49
67
 
50
- ### 2. 切换请求适配器(可选)
68
+ ### 2. 切换请求适配器
51
69
 
52
- 默认使用 Axios 适配器,可切换为 Fetch:
70
+ 默认使用 Fetch 适配器(浏览器环境自动检测),可手动切换:
53
71
 
54
72
  ```typescript
55
73
  import { createRequest } from "@jctrans-materials/shared";
56
74
 
57
- // 切换为 Fetch 适配器
58
- createRequest("fetch", {
59
- fetch: window.fetch, // 可传入自定义 fetch 实现(如 node-fetch)
75
+ // 切换为 Axios
76
+ createRequest("axios");
77
+
78
+ // 切换为 Fetch(可传入自定义实现,如 Nuxt 的 $fetch)
79
+ createRequest("fetch", { fetch: $fetch });
80
+
81
+ // 切换为 Custom(小程序等非浏览器环境,完全透传)
82
+ createRequest("custom", {
83
+ baseURL: "https://cloudapi-sit2.jctrans.net.cn",
84
+ fetch: async ({ url, method, data, params, headers }) => {
85
+ // 用户自己的请求实现,header/token/返回值完全由你控制
86
+ return new Promise((resolve, reject) => {
87
+ wx.request({
88
+ url,
89
+ method: method as any,
90
+ data,
91
+ header: headers,
92
+ success: (res: any) => resolve(res.data),
93
+ fail: reject,
94
+ });
95
+ });
96
+ },
60
97
  });
61
98
  ```
62
99
 
63
- ### 3. 基础使用示例
100
+ #### ClientId 插件
64
101
 
65
- #### 搜索国家
102
+ 自动在所有请求 header 中注入客户端唯一标识(`client-uid`),用于无埋点追踪和用户行为分析。
66
103
 
67
104
  ```typescript
68
- import { searchCountryByName } from "@jctrans-materials/shared";
105
+ import {
106
+ installClientIdPlugin,
107
+ installFetchClientIdPlugin,
108
+ getOrCreateClientId,
109
+ createRequest,
110
+ } from "@jctrans-materials/shared";
111
+
112
+ // Axios 环境:直接安装插件
113
+ installClientIdPlugin(axiosInstance, {
114
+ headerName: "client-uid", // 可选,默认 'client-uid'
115
+ });
69
116
 
70
- // 搜索名称包含"中国"的国家
71
- const result = await searchCountryByName("中国", {
72
- page: 1,
73
- size: 10,
117
+ // Fetch 环境:先创建请求实例再安装
118
+ const request = createRequest("fetch", { fetch: customFetch });
119
+ installFetchClientIdPlugin({
120
+ headerName: "client-uid", // 可选,默认 'client-uid'
74
121
  });
75
- console.log(result.records); // 归一化后的国家列表
122
+
123
+ // 手动获取 clientId
124
+ const cid = getOrCreateClientId(); // 首次调用自动生成并持久化
76
125
  ```
77
126
 
78
- #### 按ID查询城市
127
+ > **原理**:使用 `nanoid` 生成,完全 SSR 兼容(无 `window`/`document` 依赖)。ClientId 存储在 `localStorage` 中,跨页面共享。
128
+
129
+ ### 3. 地理位置搜索
130
+
131
+ #### V2 接口(推荐)
79
132
 
80
133
  ```typescript
81
- import { getById } from "@jctrans-materials/shared";
134
+ import { locationSearchV2 } from "@jctrans-materials/shared";
82
135
 
83
- // 查询 ID 为 1001 的城市
84
- const city = await getById("City", 1001);
85
- console.log(city); // 城市详情
86
- ```
136
+ // 按名称搜索港口
137
+ const ports = await locationSearchV2.seaport.searchByName({
138
+ keyword: "上海",
139
+ countryId: 1,
140
+ current: 1,
141
+ size: 10,
142
+ });
87
143
 
88
- #### 获取指定国家下的所有城市
144
+ // 按 ID 查询国家
145
+ const countries = await locationSearchV2.country.getByIds([1, 2, 3]);
89
146
 
90
- ```typescript
91
- import { getCitiesByCountry } from "@jctrans-materials/shared";
147
+ // 获取国家下所有城市
148
+ const cities = await locationSearchV2.city.getCitiesByCountry(1);
149
+
150
+ // 获取城市下的港口/机场
151
+ const children = await locationSearchV2.getChildrenByCity(100, ["Seaport", "Airport"]);
152
+
153
+ // 搜索船公司
154
+ const carriers = await locationSearchV2.carrier.searchShipping({
155
+ keyword: "MAERSK",
156
+ current: 1,
157
+ size: 10,
158
+ });
92
159
 
93
- // 获取国家 ID 为 1 的所有城市
94
- const cities = await getCitiesByCountry(1, {
95
- page: 1,
96
- size: 100,
160
+ // 搜索航线
161
+ const lines = await locationSearchV2.line.searchShipping({
162
+ keyword: "AE1",
163
+ current: 1,
164
+ size: 10,
97
165
  });
98
- console.log(cities.records);
166
+
167
+ // 通用按类型+ID查询
168
+ const result = await locationSearchV2.searchByIdWithType(100, "City");
99
169
  ```
100
170
 
101
- #### 使用 V2 版本接口(推荐)
171
+ #### V1 接口(兼容旧项目)
102
172
 
103
173
  ```typescript
104
- import { locationSearchV2 } from "@jctrans-materials/shared";
174
+ import {
175
+ searchCountryByName,
176
+ searchCityByName,
177
+ searchSeaportByName,
178
+ getById,
179
+ getByIds,
180
+ getCitiesByCountry,
181
+ } from "@jctrans-materials/shared";
182
+
183
+ // 按名称搜索
184
+ const countries = await searchCountryByName("中国");
185
+
186
+ // 按 ID 查询
187
+ const city = await getById("City", 1001);
188
+ const ports = await getByIds("Seaport", [100, 200]);
105
189
 
106
- // 搜索港口
107
- const seaports = await locationSearchV2.seaport.searchByName({
108
- keyword: "上海",
109
- countryId: 1,
110
- current: 1,
111
- size: 10,
190
+ // 按国家获取城市
191
+ const cities = await getCitiesByCountry(1);
192
+ ```
193
+
194
+ ### 4. 认证 API
195
+
196
+ ```typescript
197
+ import {
198
+ loginApi,
199
+ sendEmailCodeApi,
200
+ loginByFacebookApi,
201
+ autoLoginByTGC,
202
+ resetPasswordApi,
203
+ setAuthSessionItems,
204
+ getAuthSessionItems,
205
+ } from "@jctrans-materials/shared";
206
+
207
+ // 登录
208
+ const res = await loginApi({
209
+ username: "user@example.com",
210
+ password: "encrypted_password",
211
+ appId: "ERA",
212
+ code: "",
213
+ uuid: "",
214
+ kick: false,
215
+ redirectPath: "/dashboard",
216
+ activityCode: "",
217
+ referenceUserId: "",
112
218
  });
113
- console.log(seaports.records);
219
+
220
+ // 发送邮箱验证码
221
+ await sendEmailCodeApi({ target: "user@example.com" });
222
+
223
+ // 第三方登录
224
+ const fbResult = await loginByFacebookApi({ /* oauth data */ });
225
+
226
+ // TGC 自动登录
227
+ await autoLoginByTGC({ toSys: "ERA", path: "/", tgc: "tgc_token" });
228
+
229
+ // 密码找回
230
+ await resetPasswordApi({ account: "user@example.com", password: "new_pwd", code: "123456" });
231
+
232
+ // Session 管理
233
+ setAuthSessionItems({ activityCode: "SUMMER2026", path: "/register" });
234
+ const items = getAuthSessionItems();
114
235
  ```
115
236
 
116
- ## 核心 API 文档
237
+ ### 5. Token 管理
117
238
 
118
- ### 配置管理
239
+ ```typescript
240
+ import {
241
+ setTokenAll,
242
+ getToken,
243
+ getRefreshToken,
244
+ getExpiresTimeIn,
245
+ clearAllAuth,
246
+ setRememberMe,
247
+ } from "@jctrans-materials/shared";
248
+
249
+ // 登录成功后保存 Token
250
+ setTokenAll({
251
+ accessToken: "eyJhbGci...",
252
+ expireIn: 86400,
253
+ refreshToken: "refresh_token...",
254
+ refreshTokenExpireIn: 2592000,
255
+ });
119
256
 
120
- | 方法 | 说明 | 类型 |
121
- | ------------------ | ---------------------- | -------------------------------------------- |
122
- | `initSharedConfig` | 初始化全局配置 | `(newConfig: Partial<SharedConfig>) => void` |
123
- | `getSharedConfig` | 获取当前配置 | `() => { basePath: string }` |
124
- | `getIsEn` | 判断当前是否为英文环境 | `() => boolean` |
257
+ // 读取 Token
258
+ const token = getToken();
125
259
 
126
- ### 基础搜索(baseSearch)
260
+ // 清除所有凭证
261
+ clearAllAuth();
127
262
 
128
- | 方法 | 说明 |
129
- | --------------------- | ---------------------------------- |
130
- | `search` | 通用搜索接口(支持多条件、多类型) |
131
- | `searchByName` | 按名称搜索指定类型地理实体 |
132
- | `getByIds` | 按类型+ID批量查询 |
133
- | `getById` | 按类型+单个ID查询 |
134
- | `getCitiesByCountry` | 获取指定国家下的所有城市 |
135
- | `getChildrenByCity` | 获取指定城市下的港口/机场 |
136
- | `searchCountryByName` | 按名称搜索国家 |
137
- | `searchCityByName` | 按名称搜索城市(支持国家筛选) |
138
- | `searchSeaportByName` | 按名称搜索港口(支持城市筛选) |
139
- | `searchAirportByName` | 按名称搜索机场(支持城市筛选) |
263
+ // 记住账号(⚠️ 密码会明文存储在 Cookie 中)
264
+ setRememberMe("user@example.com", "password", true);
265
+ ```
140
266
 
141
- ### V2 版本搜索(searchV2)
267
+ ### 6. 加密
142
268
 
143
- `locationSearchV2` 提供更语义化的接口封装:
269
+ ```typescript
270
+ import { encrypt, decrypt, Encrypt, Decrypt } from "@jctrans-materials/shared";
144
271
 
145
- | 子模块 | 方法 | 说明 |
146
- | --------- | ---------------------------------------------- | -------------------------- |
147
- | `country` | `searchByName`/`getByIds` | 国家搜索/批量查询 |
148
- | `region` | `searchByName`/`getByIds` | 地区搜索/批量查询 |
149
- | `city` | `searchByName`/`getByIds`/`getCitiesByCountry` | 城市搜索/查询/按国家筛选 |
150
- | `seaport` | `searchByName`/`getByIds` | 港口搜索/批量查询 |
151
- | `airport` | `searchByName`/`getByIds` | 机场搜索/批量查询 |
152
- | `wharf` | `getByIds` | 码头批量查询 |
153
- | - | `searchByIdWithType` | 按类型+ID查询任意地理实体 |
154
- | - | `getChildrenByCity` | 获取城市下的港口/机场/码头 |
272
+ // camelCase(推荐)
273
+ const encrypted = encrypt("plain_text");
274
+ const decrypted = decrypt(encrypted);
155
275
 
156
- ### 请求适配器
276
+ // PascalCase(兼容旧代码)
277
+ const enc = Encrypt("plain_text");
278
+ const dec = Decrypt(enc);
279
+ ```
157
280
 
158
- | 方法 | 说明 |
159
- | --------------- | ---------------------------------- |
160
- | `createRequest` | 创建/切换请求适配器(axios/fetch) |
161
- | `request` | 全局请求实例(get/post方法) |
281
+ > ⚠️ 加密使用 AES-ECB 模式,密钥与后端对齐。ECB 模式安全性较低(相同明文产生相同密文),仅用于传输层保护,不可用于高安全场景。
162
282
 
163
- ### 事件总线
283
+ ### 7. GrowingIO 埋点
164
284
 
165
- 用于全局模态框交互:
285
+ ```typescript
286
+ import { tracker, normalizeClickPageAttrs, GIO_EVENT_CLICK_PAGE } from "@jctrans-materials/shared";
287
+
288
+ // 初始化
289
+ tracker.init("account-id", "datasource-id", {
290
+ debug: true,
291
+ trackPage: true,
292
+ // 性能采集(默认开启首屏加载监控和错误监控,默认关闭网络监控)
293
+ performance: {
294
+ monitor: true, // 首屏加载监控,默认 true
295
+ exception: true, // 错误监控,默认 true
296
+ network: { // 网络监控,不配置则默认关闭
297
+ exclude: /api\.example\.com/, // 排除指定域名的请求
298
+ },
299
+ },
300
+ });
301
+
302
+ // 设置用户 ID
303
+ tracker.setUserId("user-123");
304
+
305
+ // 埋点事件
306
+ tracker.track("order_submit", {
307
+ order_type: "freight",
308
+ price: 5000,
309
+ });
310
+
311
+ // clickPage 事件(自动补齐必填字段)
312
+ const attrs = normalizeClickPageAttrs(GIO_EVENT_CLICK_PAGE, {
313
+ pageCode_var: "HOME",
314
+ pageClickName_var: "search_btn",
315
+ });
316
+ tracker.track(GIO_EVENT_CLICK_PAGE, attrs);
317
+ ```
318
+
319
+ ### 8. Modal 事件总线
166
320
 
167
321
  ```typescript
168
322
  import { emitter, MODAL_ACTION } from "@jctrans-materials/shared";
169
323
 
170
- // 监听模态框打开事件
324
+ // 监听
171
325
  emitter.on(MODAL_ACTION.Open, () => {
172
326
  console.log("模态框已打开");
173
327
  });
174
328
 
175
- // 触发模态框关闭事件
176
- emitter.emit(MODAL_ACTION.Close);
329
+ // 触发
330
+ emitter.emit(MODAL_ACTION.Close, undefined);
331
+ emitter.emit(MODAL_ACTION.Submit, { key: "value" });
177
332
  ```
178
333
 
179
- ## 关键类型定义
334
+ ### 9. 业务 API
180
335
 
181
- ### 地理实体类型
336
+ ```typescript
337
+ import {
338
+ slideGetApi,
339
+ slideCheckApi,
340
+ findDictApi,
341
+ findPAreaCodeApi,
342
+ getLocalIpDataApi,
343
+ getRedirectPathApi,
344
+ reportNewTypeDataApi,
345
+ } from "@jctrans-materials/shared";
346
+
347
+ // 滑动验证码
348
+ const captcha = await slideGetApi({ clientUid: "xxx", ts: Date.now() });
349
+ const verified = await slideCheckApi({ pointJson: "...", uuid: "..." });
350
+
351
+ // 字典查询
352
+ const dict = await findDictApi({ size: -1 });
353
+
354
+ // 区号列表
355
+ const areaCodes = await findPAreaCodeApi({ size: -1 });
356
+
357
+ // 数据上报
358
+ await reportNewTypeDataApi({
359
+ reportData: "report_content",
360
+ reportType: "City",
361
+ });
362
+ ```
363
+
364
+ ### 10. 反查回显
365
+
366
+ 通过英文展示名(nameEnShow)反查实体详情,用于从外部链接或 URL 参数回显数据。
182
367
 
183
368
  ```typescript
184
- // baseSearch 支持的类型
185
- type DisplayInfo =
186
- | "Continent"
187
- | "Country"
188
- | "Province"
189
- | "City"
190
- | "Seaport"
191
- | "Airport";
369
+ import {
370
+ getAirportByNameEnShow,
371
+ getCountryByNameEnShow,
372
+ getCityByNameEnShow,
373
+ getSeaportByNameEnShow,
374
+ } from "@jctrans-materials/shared";
375
+
376
+ // 按英文展示名查询机场详情
377
+ const airport = await getAirportByNameEnShow("Shanghai Pudong International Airport");
192
378
 
193
- // searchV2 支持的类型(扩展)
194
- type LocationType = DisplayInfo | "Region" | "Street" | "Town" | "Wharf";
379
+ // 按英文展示名查询国家详情
380
+ const country = await getCountryByNameEnShow("China");
381
+
382
+ // 按英文展示名查询城市详情
383
+ const city = await getCityByNameEnShow("Shanghai");
384
+
385
+ // 按英文展示名查询港口详情
386
+ const seaport = await getSeaportByNameEnShow("Shanghai Port");
195
387
  ```
196
388
 
197
- ### 归一化实体结构
389
+ ### 11. DW-SDK(数据仓库 GraphQL SDK)
390
+
391
+ 对 `@jctrans/dw-sdk` 的零配置封装,自动注入 baseUrl 和 Authorization Token。
198
392
 
199
393
  ```typescript
200
- interface UnifiedItem {
201
- id: number | string; // 实体ID
202
- type: DisplayInfo; // 实体类型
203
- nameCn?: string; // 中文名称
204
- nameEn?: string; // 英文名称
205
- display?: string; // 适配当前语言的展示文本
206
- continent?: Record<string, any>; // 所属大洲
207
- country?: Record<string, any>; // 所属国家
208
- city?: Record<string, any>; // 所属城市
209
- province?: Record<string, any>; // 所属省份
210
- raw?: any; // 原始后端数据
394
+ import {
395
+ queryOpportunityBoard,
396
+ GraphQLClientError,
397
+ } from "@jctrans-materials/shared";
398
+ import type {
399
+ OpportunityBoardParams,
400
+ BoardQueryResult,
401
+ DateType,
402
+ } from "@jctrans-materials/shared";
403
+
404
+ // 查询商机有效看板数据
405
+ const result = await queryOpportunityBoard({
406
+ compId: 12345,
407
+ dateType: "QUARTER",
408
+ dateValue: "2026-Q2",
409
+ });
410
+
411
+ if (result.success) {
412
+ console.log(result.data.records);
413
+ } else {
414
+ console.error(result.error);
211
415
  }
212
416
  ```
213
417
 
214
- ### 请求配置
418
+ > **原理**:每次调用内部新建 `createDwSdk()` 实例,调用后即销毁,无状态污染。`baseUrl` 自动从 `initSharedConfig` 的 `prefixPath + '/data'` 拼接。
419
+
420
+ ## 核心 API 参考
421
+
422
+ ### 配置
423
+
424
+ | 方法 | 说明 | 签名 |
425
+ |------|------|------|
426
+ | `initSharedConfig` | 初始化全局配置 | `(config: Partial<SharedConfig>) => void` |
427
+ | `getSharedConfig` | 获取当前配置 | `() => SharedConfig` |
428
+ | `getIsEn` | 判断当前是否英文环境 | `() => boolean` |
429
+ | `initCommonKeys` | 初始化 Cookie Key 配置 | `(keys: Partial<CommonKeysConfig>) => void` |
430
+
431
+ ### 搜索 V2(locationSearchV2)
432
+
433
+ | 子模块 | 方法 | 说明 |
434
+ |--------|------|------|
435
+ | `country` | `searchByName` / `getByIds` | 国家 |
436
+ | `region` | `searchByName` / `getByIds` | 地区(复用 Country 接口) |
437
+ | `city` | `searchByName` / `getByIds` / `getCitiesByCountry` | 城市 |
438
+ | `province` | `searchByName` / `getByIds` | 省份 |
439
+ | `continent` | `searchByName` / `getByIds` | 大洲 |
440
+ | `seaport` | `searchByName` / `getByIds` | 港口 |
441
+ | `airport` | `searchByName` / `getByIds` | 机场 |
442
+ | `wharf` | `getByIds` | 码头 |
443
+ | `carrier` | `searchShipping` / `searchAir` / `search` / `getByIds` | 船公司/航空公司 |
444
+ | `line` | `searchShipping` / `searchAir` / `search` / `getByIds` / `searchAllByCountry` | 航线 |
445
+ | — | `searchByName` | 通用按名称搜索 |
446
+ | — | `searchByIdWithType` | 按类型+ID查询任意实体 |
447
+ | — | `getCitiesByCountry` | 国家→城市 |
448
+ | — | `getChildrenByCity` | 城市→港口/机场/码头 |
449
+
450
+ ### 搜索 V1(baseSearch)
451
+
452
+ | 方法 | 说明 |
453
+ |------|------|
454
+ | `search` | 通用搜索(支持 keyword + ids + scope) |
455
+ | `searchByName` | 按名称搜索 |
456
+ | `getByIds` / `getById` | 按 ID 查询 |
457
+ | `getCitiesByCountry` | 国家→城市 |
458
+ | `getChildrenByCity` | 城市→港口/机场 |
459
+ | `searchCountryByName` / `searchCityByName` / `searchSeaportByName` / `searchAirportByName` | 按名称快捷搜索 |
460
+ | `getContinent` / `getCountry` / `getCity` / `getProvince` / `getSeaport` / `getAirport` | 按类型获取列表 |
461
+
462
+ ### 认证
463
+
464
+ | 方法 | 说明 |
465
+ |------|------|
466
+ | `loginApi` | 账号密码登录 |
467
+ | `loginByEmailOrMobileCodeApi` | 验证码登录 |
468
+ | `loginByQrCode` / `getLoginQrCode` | 扫码登录 |
469
+ | `autoLoginByTGC` | TGC 单点登录 |
470
+ | `loginByFacebookApi` / `loginByLinkedinApi` | 第三方登录 |
471
+ | `sendEmailCodeApi` / `sendMobileCodeApi` | 发送验证码 |
472
+ | `checkEmailCodeApi` / `checkMobileCodeApi` | 校验验证码 |
473
+ | `validateAccountExistApi` | 校验账号是否存在 |
474
+ | `registerApi` / `validateCompanyApi` | 注册 |
475
+ | `resetPasswordApi` / `checkAccountExistsFPApi` / `getMEVCodeApi` | 密码找回 |
476
+ | `setAuthSessionItems` / `getAuthSessionItems` / `resetAuthSessionItems` | Auth Session 管理 |
477
+ | `setGioSessionItems` / `getGioSessionItems` / `resetGioSessionItems` | GIO Session 管理 |
478
+
479
+ ### 反查回显
480
+
481
+ | 方法 | 说明 |
482
+ |------|------|
483
+ | `getAirportByNameEnShow` | 按英文展示名查询机场 |
484
+ | `getCountryByNameEnShow` | 按英文展示名查询国家 |
485
+ | `getCityByNameEnShow` | 按英文展示名查询城市 |
486
+ | `getSeaportByNameEnShow` | 按英文展示名查询港口 |
487
+
488
+ ### DW-SDK
489
+
490
+ | 方法 | 说明 |
491
+ |------|------|
492
+ | `queryOpportunityBoard` | 查询商机有效看板(分页) |
493
+
494
+ 导出类型:`OpportunityBoardParams`, `BoardQueryResult`, `DateType`, `GraphQLClientError`。
495
+
496
+ ### Token & Cookie
497
+
498
+ | 方法 | 说明 |
499
+ |------|------|
500
+ | `setTokenAll` | 批量写入所有凭证 |
501
+ | `getToken` / `getRefreshToken` | 读取 Token |
502
+ | `getExpiresTimeIn` | 获取 Token 过期时间戳 |
503
+ | `clearAllAuth` | 清除所有认证 Cookie |
504
+ | `setRememberMe` / `getRememberAccount` | 记住账号 |
505
+
506
+ ### 工具
507
+
508
+ | 方法 | 说明 |
509
+ |------|------|
510
+ | `isEmpty` | 判断值是否为空(null/undefined/空字符串/空数组/空对象/空 Map/空 Set)。数字、布尔值等原始类型返回 `false` |
511
+ | `isIpAddress` / `getFirstDomain` | 域名工具 |
512
+ | `getAppId` / `getProjectId` | 跨环境 ID 获取 |
513
+ | `getLanguage` | 获取当前语言 |
514
+ | `getSessionHeaderValue` / `toSafeHeaderValue` | HTTP Header 安全工具 |
515
+ | `encrypt` / `decrypt` | AES 加解密 |
516
+ | `generateClientId` / `getOrCreateClientId` | 客户端唯一标识 |
517
+ | `installClientIdPlugin` | 为 Axios 实例注入 client-uid header |
518
+ | `installFetchClientIdPlugin` | 为 Fetch 请求注入 client-uid header |
519
+
520
+ ## 类型定义
521
+
522
+ ### LocationType / DisplayInfo
215
523
 
216
524
  ```typescript
217
- interface RequestConfig {
218
- params?: Record<string, any>; // URL参数
219
- headers?: Record<string, string>; // 请求头
525
+ type LocationType =
526
+ | "Continent" | "Country" | "Region" | "Province"
527
+ | "City" | "Seaport" | "Airport"
528
+ | "Street" | "Town" | "Wharf"
529
+ | "Carrier" | "Line";
530
+
531
+ type DisplayInfo = LocationType; // 兼容别名
532
+ ```
533
+
534
+ ### LocationUnifiedItem / UnifiedItem
535
+
536
+ ```typescript
537
+ interface LocationUnifiedItem {
538
+ id: number | string;
539
+ type: LocationType;
540
+ nameCn?: string;
541
+ nameEn?: string;
542
+ display?: string; // 适配当前语言的展示文本
543
+ displayEn?: string; // 英文展示
544
+ displayCn?: string; // 中文展示
545
+ code?: string; // 通用代码(Carrier: scacCode, 机场: iataCode)
546
+ scacCode?: string; // Carrier: SCAC 代码
547
+ iataCode?: string; // Airport: IATA 代码
548
+ carrierCode?: string; // Carrier: 船公司代码
549
+ lineType?: string; // Carrier/Line: air | shipping
550
+ country?: Record<string, any>;
551
+ city?: Record<string, any>;
552
+ province?: Record<string, any>;
553
+ continent?: Record<string, any>;
554
+ raw?: any; // 原始后端数据
220
555
  }
556
+
557
+ type UnifiedItem = LocationUnifiedItem; // 兼容别名
221
558
  ```
222
559
 
223
- ## 注意事项
560
+ ### BaseResponse
224
561
 
225
- 1. 接口默认分页:`current=1`(页码)、`size=10`(每页条数)
226
- 2. 多语言判断优先级:Nuxt Cookie > 浏览器 Cookie > Nuxt SSR 上下文
227
- 3. 实体展示文本规则:
228
- - 港口/机场:`名称 (城市, 国家)`
229
- - 城市/省份:`名称 (国家)`
230
- - 国家:`名称 (大洲)`
231
- 4. 请求超时时间(Axios):默认 15000ms
232
- 5. 重复数据:SDK 内部会自动根据 `type + id` 去重
562
+ ```typescript
563
+ interface BaseResponse<T = any> {
564
+ records: T[];
565
+ total: number;
566
+ size: number;
567
+ current: number;
568
+ pages?: number;
569
+ }
570
+ ```
571
+
572
+ ## 架构说明
573
+
574
+ ### 请求拦截器
575
+
576
+ 请求适配器(Axios / Fetch)共享以下拦截器逻辑:
577
+
578
+ - **buildHeaders** — 统一注入 APP-ID、Accept-Language、pageCode、Authorization、popular-channel
579
+ - **handleTokenRefresh** — Access Token 即将过期时(5 分钟内),自动使用 Refresh Token 刷新
580
+ - **handleResponse** — 登录接口自动同步 Token、验证码接口触发 GIO 埋点、401 触发回调
581
+
582
+ ### 多语言判断优先级
583
+
584
+ 1. Nuxt `useCookie("jc-language")`
585
+ 2. 浏览器 Cookie `jc-language`
586
+ 3. Nuxt SSR 请求头中的 Cookie
587
+
588
+ ### 实体展示文本规则
589
+
590
+ | 类型 | 格式 | 示例 |
591
+ |------|------|------|
592
+ | 港口/机场/码头 | `名称 (城市, 国家)` | Shanghai (Shanghai, China) |
593
+ | 城市/省份 | `名称 (国家)` | Shanghai (China) |
594
+ | 国家 | `名称 (大洲)` | China (Asia) |
595
+ | 船公司 | `名称 (代码)` | MAERSK (MAEU) |
596
+ | 航线 | `名称 (国家)` | AE1 (China) |
597
+
598
+ ## 迁移指南
599
+
600
+ ### 从 V1 (baseSearch) 迁移到 V2 (searchV2)
601
+
602
+ V2 是 V1 的超集,所有 V1 函数仍然可用。推荐逐步迁移:
603
+
604
+ ```typescript
605
+ // V1 写法
606
+ import { searchCityByName } from "@jctrans-materials/shared";
607
+ const result = await searchCityByName("上海", { countryId: 1 });
608
+
609
+ // V2 等价写法
610
+ import { locationSearchV2 } from "@jctrans-materials/shared";
611
+ const result = await locationSearchV2.city.searchByName({
612
+ keyword: "上海",
613
+ countryId: 1,
614
+ });
615
+ ```
616
+
617
+ V2 新增能力:Carrier(船公司/航空公司)、Line(航线)、Province、Continent 命名空间。
618
+
619
+ ### crypto 函数名变更
620
+
621
+ ```typescript
622
+ // 旧写法(仍然可用)
623
+ import { Encrypt, Decrypt } from "@jctrans-materials/shared";
624
+
625
+ // 新写法(推荐)
626
+ import { encrypt, decrypt } from "@jctrans-materials/shared";
627
+ ```
233
628
 
234
629
  ## 许可证
235
630
 
236
- [MIT](根据实际情况修改)
631
+ MIT