lgsso-sdk 1.2.7 → 1.3.0

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.
@@ -1,8 +1,8 @@
1
1
  /*
2
2
  * @Author: Robin LEI
3
3
  * @Date: 2025-08-21 15:09:15
4
- * @LastEditTime: 2025-08-21 15:22:11
5
- * @FilePath: \lg-wms-admind:\业务代码\中联钢信\五服一管\lg-ssosdk\src\utils.js
4
+ * @LastEditTime: 2026-01-23 11:28:26
5
+ * @FilePath: \lims-frontd:\业务代码\中联钢信\五服一管\lg-ssosdk\src\utils.js
6
6
  */
7
7
  /**
8
8
  * 检查是否在浏览器环境
@@ -38,38 +38,58 @@ function getQueryParam(name, url) {
38
38
  }
39
39
 
40
40
  /**
41
- * 移除URL中的指定参数
42
- * @param {string} name - 参数名
41
+ * 移除URL中的指定参数(支持单个或多个)
42
+ * @param {string|string[]} names - 要删除的参数名(单个字符串或字符串数组)
43
43
  */
44
- function removeQueryParam(name) {
44
+ function removeQueryParam(names) {
45
45
  if (!isBrowser()) return;
46
46
 
47
+ // 统一转为数组,兼容单个参数的调用方式
48
+ const paramNames = Array.isArray(names) ? names : [names];
49
+ if (paramNames.length === 0) return; // 无参数需删除时直接返回
50
+
47
51
  const url = new URL(window.location.href);
48
- let params, newUrl;
52
+ let newUrl;
49
53
 
50
54
  // 处理hash模式(参数在#之后)
51
55
  if (url.hash.includes('?')) {
52
56
  const [hashPath, hashQuery] = url.hash.split('?');
53
- params = new URLSearchParams(hashQuery);
57
+ const params = new URLSearchParams(hashQuery);
58
+
59
+ // 批量删除参数
60
+ paramNames.forEach(name => {
61
+ if (params.has(name)) {
62
+ params.delete(name);
63
+ }
64
+ });
54
65
 
55
- if (params.has(name)) {
56
- params.delete(name);
57
- const newHash = params.toString() ? `${hashPath}?${params.toString()}` : hashPath;
66
+ // 重构hash部分
67
+ const newHash = params.toString() ? `${hashPath}?${params.toString()}` : hashPath;
68
+ if (newHash !== url.hash) { // 只有hash变化时才更新URL
58
69
  url.hash = newHash;
59
70
  newUrl = url.toString();
60
71
  }
61
72
  }
62
73
  // 处理history模式(参数在?之后)
63
74
  else {
64
- params = new URLSearchParams(url.search);
65
- if (params.has(name)) {
66
- params.delete(name);
75
+ const params = new URLSearchParams(url.search);
76
+ let hasChanged = false;
77
+
78
+ // 批量删除参数
79
+ paramNames.forEach(name => {
80
+ if (params.has(name)) {
81
+ params.delete(name);
82
+ hasChanged = true;
83
+ }
84
+ });
85
+
86
+ // 只有参数变化时才更新URL
87
+ if (hasChanged) {
67
88
  url.search = params.toString();
68
89
  newUrl = url.toString();
69
90
  }
70
91
  }
71
-
72
- // 更新URL
92
+ // 更新URL(仅当URL有变化时)
73
93
  if (newUrl && newUrl !== window.location.href) {
74
94
  window.location.replace(newUrl);
75
95
  }
@@ -92,6 +112,7 @@ function getCurrentUrlWithParams() {
92
112
  */
93
113
  function mergeConfigs(defaults, options) {
94
114
  if (!options) return { ...defaults };
115
+
95
116
  const merged = { ...defaults };
96
117
  for (const key in options) {
97
118
  if (options.hasOwnProperty(key)) {
@@ -4036,6 +4057,93 @@ async function request(url, {
4036
4057
  }
4037
4058
  }
4038
4059
 
4060
+ // ===== Cookie操作工具函数 =====
4061
+ /**
4062
+ * 获取指定名称的Cookie值
4063
+ * @param {string} name Cookie名称
4064
+ * @returns {string|null} Cookie值
4065
+ */
4066
+ function getCookie(name) {
4067
+ if (!isBrowser()) return null;
4068
+ const match = document.cookie.match(new RegExp(`(^| )${name}=([^;]+)`));
4069
+ return match ? decodeURIComponent(match[2]) : null;
4070
+ }
4071
+
4072
+ /**
4073
+ * 设置Cookie(支持指定根域名)
4074
+ * @param {string} name Cookie名称
4075
+ * @param {string} value Cookie值
4076
+ * @param {Object} options 配置(expires: 过期天数/Date, path: 路径, domain: 域名)
4077
+ */
4078
+ function setCookie(name, value, options = {}) {
4079
+ if (!isBrowser()) return;
4080
+ let cookieStr = `${name}=${encodeURIComponent(value)}`;
4081
+
4082
+ // 路径:默认根路径,确保全站可用
4083
+ cookieStr += `; path=${options.path || '/'}`;
4084
+
4085
+ // 域名:指定根域名.zlgx.com(适配你的场景)
4086
+ // 优先级:用户传入的domain > 默认根域名.zlgx.com
4087
+ const targetDomain = options.domain || '.zlgx.com';
4088
+ cookieStr += `; domain=${targetDomain}`;
4089
+
4090
+ // 过期时间(默认1天)
4091
+ if (options.expires) {
4092
+ const expires = typeof options.expires === 'number'
4093
+ ? new Date(Date.now() + options.expires * 86400000)
4094
+ : options.expires;
4095
+ cookieStr += `; expires=${expires.toUTCString()}`;
4096
+ }
4097
+
4098
+ // 可选:HTTPS环境下添加Secure(仅HTTPS传输)
4099
+ if (options.secure || window.location.protocol === 'https:') {
4100
+ cookieStr += '; Secure';
4101
+ }
4102
+
4103
+ // 可选:防止XSS攻击的HttpOnly(注意:HttpOnly的Cookie无法通过JS读取)
4104
+ if (options.httpOnly) {
4105
+ cookieStr += '; HttpOnly';
4106
+ }
4107
+
4108
+ document.cookie = cookieStr;
4109
+ }
4110
+
4111
+ function removeCookie(name, options = {}) {
4112
+ if (!isBrowser()) return;
4113
+ setCookie(
4114
+ name,
4115
+ '',
4116
+ {
4117
+ expires: -1, // 立即过期
4118
+ path: options.path || '/',
4119
+ domain: options.domain || '.zlgx.com' // 必须和设置时一致
4120
+ }
4121
+ );
4122
+ }
4123
+
4124
+ // 调用示例:删除根域名下的platType
4125
+ removeCookie('platType', { domain: '.zlgx.com' });
4126
+
4127
+ /**
4128
+ * 给重定向地址拼接platType参数
4129
+ * @param {string} redirectUrl 原始重定向地址
4130
+ * @param {string} platType platType值
4131
+ * @param {string} platTypeKey 参数名(默认platType)
4132
+ * @returns {string} 拼接后的重定向地址
4133
+ */
4134
+ function addPlatTypeToRedirectUrl(redirectUrl, platType, platTypeKey = 'platType') {
4135
+ if (!redirectUrl || !platType) return redirectUrl;
4136
+ try {
4137
+ const url = new URL(redirectUrl);
4138
+ url.searchParams.set(platTypeKey, platType);
4139
+ return url.toString();
4140
+ } catch (e) {
4141
+ // 兼容非标准URL的情况(比如相对路径)
4142
+ const separator = redirectUrl.includes('?') ? '&' : '?';
4143
+ return `${redirectUrl}${separator}${platTypeKey}=${platType}`;
4144
+ }
4145
+ }
4146
+
4039
4147
  // 默认配置
4040
4148
  const DEFAULT_CONFIG = {
4041
4149
  accessCodeKey: 'accessCode',
@@ -4046,13 +4154,14 @@ const DEFAULT_CONFIG = {
4046
4154
  refreshCodeApi: '',
4047
4155
  logoutApi: '',
4048
4156
  logOutUrl: '',
4049
- storage: localStorage, // 可替换为sessionStorage
4157
+ storage: localStorage,
4050
4158
  oldPwdKey: 'oldPwd',
4051
4159
  newPwdKey: 'newPwd',
4052
4160
  changePasswordApi: '',
4053
4161
  sendCaptchaCodeApi: '',
4054
4162
  typeKey: 'type',
4055
- codeKey: 'code'
4163
+ codeKey: 'code',
4164
+ platTypeKey: 'platType' // platType参数名配置
4056
4165
  };
4057
4166
 
4058
4167
  let config = null;
@@ -4075,46 +4184,35 @@ function validateConfig(options) {
4075
4184
  }
4076
4185
 
4077
4186
  /**
4078
- * 判断是否是iOS移动端(精准适配iPad Air/所有iPad机型 + 全版本iPadOS)
4187
+ * 判断是否是iOS移动端
4079
4188
  * @returns {boolean} 是否为iOS环境
4080
4189
  */
4081
4190
  function isIOS() {
4082
- // 非浏览器环境直接返回false(如需保留请取消注释)
4083
- // if (!isBrowser()) return false;
4191
+ if (!isBrowser()) return false;
4084
4192
 
4085
4193
  const userAgent = navigator.userAgent.toLowerCase();
4086
- const platform = navigator.platform.toLowerCase(); // 统一转小写,避免大小写兼容问题
4194
+ const platform = navigator.platform.toLowerCase();
4087
4195
  const maxTouchPoints = navigator.maxTouchPoints || 0;
4088
- const screenRatio = screen.width / screen.height; // 屏幕宽高比(iPad核心特征:4:3左右)
4196
+ const screenRatio = screen.width / screen.height;
4089
4197
 
4090
- // ========== 核心检测规则 ==========
4091
- // 1. 基础规则:iPhone/iPod 直接命中(无兼容问题)
4092
4198
  const isIphoneIpod = /iphone|ipod/.test(userAgent) && !window.MSStream;
4093
-
4094
- // 2. iPad 专属检测(覆盖所有iPad机型,含iPad Air)
4095
4199
  const isIpad = (
4096
- // 场景1:老版本iPadOS/原生标识(platform含ipad)
4097
4200
  platform.includes('ipad') ||
4098
- // 场景2:iPadOS 13+ 伪装Mac(UA含macintosh + 触摸+ 非Mac平台)
4099
4201
  (
4100
- userAgent.includes('macintosh') &&
4101
- maxTouchPoints > 0 && // iPad Air 触摸点≥5,Mac几乎为0
4102
- !platform.includes('mac') && // 排除真Mac
4202
+ userAgent.includes('macintosh') &&
4203
+ maxTouchPoints > 0 &&
4204
+ !platform.includes('mac') &&
4103
4205
  !window.MSStream
4104
4206
  ) ||
4105
- // 场景3:新版本iPadOS(UA直接含ipados,如iPad Air搭载的iPadOS 15+)
4106
4207
  userAgent.includes('ipados') ||
4107
- // 场景4:极端场景(第三方浏览器如Chrome for iPad Air)
4108
4208
  (
4109
- maxTouchPoints >= 5 && // iPad Air 固定支持5点触摸
4110
- !platform.includes('android') && // 排除安卓平板
4111
- (screenRatio > 0.7 && screenRatio < 1.4) // iPad宽高比≈0.75(4:3),Mac多为1.78(16:9)
4209
+ maxTouchPoints >= 5 &&
4210
+ !platform.includes('android') &&
4211
+ (screenRatio > 0.7 && screenRatio < 1.4)
4112
4212
  )
4113
4213
  );
4114
4214
 
4115
- // ========== 最终判定 ==========
4116
- const result = isIphoneIpod || isIpad;
4117
- return result;
4215
+ return isIphoneIpod || isIpad;
4118
4216
  }
4119
4217
 
4120
4218
  /**
@@ -4134,9 +4232,17 @@ function createSSO() {
4134
4232
  config = mergeConfigs(DEFAULT_CONFIG, options);
4135
4233
  validateConfig(config);
4136
4234
 
4235
+ // 初始化时检测URL中的platType并存入Cookie
4236
+ if (isBrowser()) {
4237
+ const platTypeFromUrl = getQueryParam(config.platTypeKey);
4238
+ if (platTypeFromUrl) {
4239
+ setCookie(config.platTypeKey, platTypeFromUrl);
4240
+ // 可选:移除URL中的platType(避免重复显示)
4241
+ }
4242
+ }
4243
+
4137
4244
  const accessCode = getQueryParam(config.accessCodeKey);
4138
4245
 
4139
- // 验证必要的API配置
4140
4246
  if (!config.tokenApi) {
4141
4247
  return { code: -100, msg: '缺少tokenApi配置', success: false };
4142
4248
  }
@@ -4155,14 +4261,25 @@ function createSSO() {
4155
4261
 
4156
4262
  if (result.code === 0 && result.data) {
4157
4263
  config.storage.setItem(config.tokenKey, result.data);
4158
- removeQueryParam(config.accessCodeKey);
4264
+ removeQueryParam([config.accessCodeKey,config.platTypeKey]);
4159
4265
  }
4160
4266
  return result;
4161
4267
  }
4162
4268
  // 如果没有token,跳转到登录页
4163
4269
  else if (!this.getToken()) {
4164
4270
  if (isBrowser() && config.logOutUrl) {
4165
- window.location.href = `${config.logOutUrl}?redirect_uri=${encodeURIComponent(getCurrentUrlWithParams())}`;
4271
+ // ===== 核心修正:platType拼到redirect_uri里 =====
4272
+ const platTypeFromCookie = getCookie(config.platTypeKey);
4273
+ // 1. 获取原始重定向地址
4274
+ let redirectUri = getCurrentUrlWithParams();
4275
+ // 2. 给重定向地址加platType参数
4276
+ if (platTypeFromCookie) {
4277
+ redirectUri = addPlatTypeToRedirectUrl(redirectUri, platTypeFromCookie, config.platTypeKey);
4278
+ }
4279
+ // 3. 构建登录页URL(仅带redirect_uri参数)
4280
+ let loginUrl = new URL(config.logOutUrl);
4281
+ loginUrl.searchParams.set('redirect_uri', encodeURIComponent(redirectUri));
4282
+ window.location.href = loginUrl.toString();
4166
4283
  }
4167
4284
  }
4168
4285
 
@@ -4194,33 +4311,21 @@ function createSSO() {
4194
4311
  * @returns {Promise<Object>} 接口返回结果
4195
4312
  */
4196
4313
  async logout() {
4197
- // 1. 前置校验:初始化状态
4198
4314
  if (!config) {
4199
4315
  return { code: -101, msg: '请先调用init方法初始化', success: false };
4200
4316
  }
4201
4317
 
4202
- // 2. 工具函数:统一构建登录跳转URL(确保完整编码当前URL的所有参数/hash)
4203
- const buildLoginUrl = () => {
4204
- if (!config.logOutUrl || !isBrowser()) return '';
4205
- // 关键:获取当前完整URL(含query、hash)并完整编码,避免参数丢失
4206
- const currentFullUrl = window.location.href;
4207
- const encodedRedirectUri = encodeURIComponent(currentFullUrl);
4208
- return `${config.logOutUrl}?redirect_uri=${encodedRedirectUri}`;
4209
- };
4210
-
4211
- // 3. 校验logoutApi配置(仅接口调用时需要,跳转登录页不受此影响)
4212
4318
  if (!config.logoutApi) {
4213
- // 无logoutApi时仍清除token并跳转登录页,保证基础退出逻辑
4214
- this.removeToken();
4215
- const loginUrl = buildLoginUrl();
4216
- if (loginUrl) window.location.href = loginUrl;
4217
4319
  return { code: -102, msg: '未配置logoutApi', success: false };
4218
4320
  }
4219
4321
 
4322
+ // 获取并删除Cookie中的platType
4323
+ const platType = getCookie(config.platTypeKey);
4324
+ removeCookie(config.platTypeKey);
4325
+
4220
4326
  const token = this.getToken();
4221
4327
  if (token) {
4222
4328
  try {
4223
- // 调用退出接口
4224
4329
  const result = await request(
4225
4330
  config.logoutApi,
4226
4331
  {
@@ -4229,37 +4334,48 @@ function createSSO() {
4229
4334
  timeout: config.timeout
4230
4335
  }
4231
4336
  );
4232
- // 无论接口返回结果如何,都清除token
4233
4337
  this.removeToken();
4234
4338
 
4235
- // 跳转登录页(携带完整的当前URL)
4236
- const loginUrl = buildLoginUrl();
4237
- if (loginUrl) window.location.href = loginUrl;
4339
+ if (isBrowser() && config.logOutUrl) {
4340
+ // ===== 核心修正:platType拼到redirect_uri里 =====
4341
+ // 1. 获取原始重定向地址
4342
+ let redirectUri = getCurrentUrlWithParams();
4343
+ // 2. 给重定向地址加platType参数
4344
+ if (platType) {
4345
+ redirectUri = addPlatTypeToRedirectUrl(redirectUri, platType, config.platTypeKey);
4346
+ }
4347
+ // 3. 构建登录页URL
4348
+ let logoutUrl = new URL(config.logOutUrl);
4349
+ logoutUrl.searchParams.set('redirect_uri', encodeURIComponent(redirectUri));
4350
+ window.location.href = logoutUrl.toString();
4351
+ }
4238
4352
  return result;
4239
4353
  } catch (error) {
4240
- // 接口调用失败,仍清除token并跳转登录页
4241
- this.removeToken();
4242
- const loginUrl = buildLoginUrl();
4243
- if (loginUrl) window.location.href = loginUrl;
4244
4354
  return { code: -103, msg: `退出失败: ${error.message}`, success: false };
4245
4355
  }
4246
4356
  } else {
4247
- // 无token时直接清除(兜底)并跳转登录页
4248
4357
  this.removeToken();
4249
- const loginUrl = buildLoginUrl();
4250
- if (loginUrl) window.location.href = loginUrl;
4358
+ if (isBrowser() && config.logOutUrl) {
4359
+ // ===== 核心修正:platType拼到redirect_uri里 =====
4360
+ let redirectUri = getCurrentUrlWithParams();
4361
+ if (platType) {
4362
+ redirectUri = addPlatTypeToRedirectUrl(redirectUri, platType, config.platTypeKey);
4363
+ }
4364
+ let logoutUrl = new URL(config.logOutUrl);
4365
+ logoutUrl.searchParams.set('redirect_uri', encodeURIComponent(redirectUri));
4366
+ window.location.href = logoutUrl.toString();
4367
+ }
4251
4368
  return { code: 0, msg: '已成功清除token', success: true };
4252
4369
  }
4253
4370
  },
4254
4371
 
4255
4372
  /**
4256
4373
  * 用token换取新accessCode并跳转到指定URL
4257
- * @param {string} redirectUrl - 目标跳转地址(支持带query/hash参数)
4374
+ * @param {string} redirectUrl - 目标跳转地址
4258
4375
  * @param {string} target - 当前页面:_self、新页面打开:_blank,默认当前页_self
4259
4376
  * @returns {Promise<Object>} 接口返回结果
4260
4377
  */
4261
4378
  async toUrl(redirectUrl, target = '_self') {
4262
- // 1. 前置校验:初始化状态、参数合法性
4263
4379
  if (!config) {
4264
4380
  return { code: -101, msg: '请先调用init方法初始化', success: false };
4265
4381
  }
@@ -4268,45 +4384,49 @@ function createSSO() {
4268
4384
  return { code: -104, msg: '请提供跳转地址', success: false };
4269
4385
  }
4270
4386
 
4271
- // ========== 新增:获取当前页面的platType参数 ==========
4272
- const currentPlatType = isBrowser() ? getQueryParam('platType') : '';
4273
-
4274
- // ========== 保留:platType=screen强制_self逻辑 ==========
4275
- const isPlatTypeScreen = currentPlatType === 'screen';
4276
-
4277
- // 2. 处理target参数:platType=screen > iOS > 传入的target
4278
- const finalTarget = isPlatTypeScreen
4279
- ? '_self' // platType=screen时强制_self
4280
- : (isIOS() ? '_self' : target); // 否则沿用原iOS判断逻辑
4281
-
4282
- if (!['_self', '_blank'].includes(finalTarget)) {
4283
- return { code: -108, msg: 'target参数必须是"_self"或"_blank"', success: false };
4387
+ // iOS强制_self
4388
+ if (isIOS()) {
4389
+ target = '_self';
4284
4390
  }
4285
4391
 
4286
- // 3. 工具函数:统一构建登录跳转URL(避免重复代码,确保参数完整编码)
4287
- const buildLoginUrl = (redirectUrl) => {
4288
- if (!config.logOutUrl) return '';
4289
- // 关键:encodeURIComponent 会完整编码redirectUrl的所有部分(query/hash),避免参数丢失
4290
- const encodedRedirectUri = encodeURIComponent(redirectUrl);
4291
- return `${config.logOutUrl}?redirect_uri=${encodedRedirectUri}`;
4292
- };
4392
+ // platType=screen时强制_self
4393
+ const platType = getCookie(config.platTypeKey);
4394
+ if (platType === 'screen') {
4395
+ target = '_self';
4396
+ }
4397
+
4398
+ // 验证target参数
4399
+ if (!['_self', '_blank'].includes(target)) {
4400
+ return { code: -108, msg: 'target参数必须是"_self"或"_blank"', success: false };
4401
+ }
4293
4402
 
4294
- // 4. 校验refreshCodeApi配置
4295
4403
  if (!config.refreshCodeApi) {
4296
4404
  return { code: -105, msg: '未配置refreshCodeApi', success: false };
4297
4405
  }
4298
4406
 
4299
- // 5. 获取token,无token则直接跳登录页
4300
4407
  const token = this.getToken();
4301
4408
  if (!token) {
4302
4409
  if (isBrowser() && config.logOutUrl) {
4303
- const loginUrl = buildLoginUrl(redirectUrl);
4304
- finalTarget === '_blank' ? window.open(loginUrl, '_blank') : window.location.href = loginUrl;
4410
+ // ===== 核心修正:platType拼到redirect_uri里 =====
4411
+ // 1. 给目标跳转地址加platType
4412
+ let newRedirectUrl = redirectUrl;
4413
+ if (platType) {
4414
+ newRedirectUrl = addPlatTypeToRedirectUrl(newRedirectUrl, platType, config.platTypeKey);
4415
+ }
4416
+ // 2. 构建登录页URL
4417
+ let loginUrl = new URL(config.logOutUrl);
4418
+ loginUrl.searchParams.set('redirect_uri', encodeURIComponent(newRedirectUrl));
4419
+ const loginUrlStr = loginUrl.toString();
4420
+
4421
+ if (target === '_blank') {
4422
+ window.open(loginUrlStr, '_blank');
4423
+ } else {
4424
+ window.location.href = loginUrlStr;
4425
+ }
4305
4426
  }
4306
4427
  return { code: -106, msg: '未找到有效token', success: false };
4307
4428
  }
4308
4429
 
4309
- // 6. 有token则尝试换取accessCode并跳转
4310
4430
  try {
4311
4431
  const result = await request(
4312
4432
  config.refreshCodeApi,
@@ -4318,31 +4438,48 @@ function createSSO() {
4318
4438
  );
4319
4439
 
4320
4440
  if (result.code === 0 && result.data && isBrowser()) {
4321
- // ========== 核心优化:拼接platType到目标URL ==========
4441
+ // toUrl跳转目标地址时,仅携带accessCode,不携带platType
4322
4442
  const url = new URL(redirectUrl);
4323
- // 1. 如果当前页面有platType,拼接到目标URL(覆盖原有platType)
4324
- if (currentPlatType) {
4325
- url.searchParams.set('platType', currentPlatType);
4326
- }
4327
- // 2. 拼接accessCode(保留原有逻辑)
4328
4443
  url.searchParams.set(config.accessCodeKey, result.data);
4329
4444
  const targetUrl = url.toString();
4330
4445
 
4331
- finalTarget === '_blank' ? window.open(targetUrl, '_blank') : window.location.href = targetUrl;
4446
+ if (target === '_blank') {
4447
+ window.open(targetUrl, '_blank');
4448
+ } else {
4449
+ window.location.href = targetUrl;
4450
+ }
4332
4451
  } else {
4333
- // 接口返回失败,跳登录页(复用统一的登录URL构建逻辑)
4334
- if (isBrowser() && config.logOutUrl) {
4335
- const loginUrl = buildLoginUrl(redirectUrl);
4336
- finalTarget === '_blank' ? window.open(loginUrl, '_blank') : window.location.href = loginUrl;
4452
+ // ===== 核心修正:platType拼到redirect_uri里 =====
4453
+ let newRedirectUrl = redirectUrl;
4454
+ if (platType) {
4455
+ newRedirectUrl = addPlatTypeToRedirectUrl(newRedirectUrl, platType, config.platTypeKey);
4456
+ }
4457
+ let loginUrl = new URL(config.logOutUrl);
4458
+ loginUrl.searchParams.set('redirect_uri', encodeURIComponent(newRedirectUrl));
4459
+ const loginUrlStr = loginUrl.toString();
4460
+
4461
+ if (target === '_blank') {
4462
+ window.open(loginUrlStr, '_blank');
4463
+ } else {
4464
+ window.location.href = loginUrlStr;
4337
4465
  }
4338
4466
  }
4339
4467
 
4340
4468
  return result;
4341
4469
  } catch (error) {
4342
- // 接口异常,跳登录页(复用统一的登录URL构建逻辑)
4343
- if (isBrowser() && config.logOutUrl) {
4344
- const loginUrl = buildLoginUrl(redirectUrl);
4345
- finalTarget === '_blank' ? window.open(loginUrl, '_blank') : window.location.href = loginUrl;
4470
+ // ===== 核心修正:platType拼到redirect_uri里 =====
4471
+ let newRedirectUrl = redirectUrl;
4472
+ if (platType) {
4473
+ newRedirectUrl = addPlatTypeToRedirectUrl(newRedirectUrl, platType, config.platTypeKey);
4474
+ }
4475
+ let loginUrl = new URL(config.logOutUrl);
4476
+ loginUrl.searchParams.set('redirect_uri', encodeURIComponent(newRedirectUrl));
4477
+ const loginUrlStr = loginUrl.toString();
4478
+
4479
+ if (target === '_blank') {
4480
+ window.open(loginUrlStr, '_blank');
4481
+ } else {
4482
+ window.location.href = loginUrlStr;
4346
4483
  }
4347
4484
  return { code: -107, msg: `跳转失败: ${error.message}`, success: false };
4348
4485
  }
@@ -4381,7 +4518,7 @@ function createSSO() {
4381
4518
  * @returns {Object|null} 当前配置
4382
4519
  */
4383
4520
  getConfig() {
4384
- return { ...config }; // 返回配置的副本,防止外部修改
4521
+ return { ...config };
4385
4522
  },
4386
4523
 
4387
4524
  async changePassword(fromData = {}) {
@@ -4410,7 +4547,6 @@ function createSSO() {
4410
4547
 
4411
4548
  async getPhoneCode() {
4412
4549
  const token = this.getToken();
4413
- // 修复笔误:sendCaptchaCode → sendCaptchaCodeApi
4414
4550
  return request(
4415
4551
  config.sendCaptchaCodeApi,
4416
4552
  {