autoLogin.html 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <title>自动登录</title>
  7. <script src="/js/mp_base/base.js"></script>
  8. </head>
  9. <body>
  10. <!-- 页面加载状态 -->
  11. <div id="page-loading" class="page-loading">
  12. <div class="loading-content">
  13. <div class="loading-spinner"></div>
  14. <div class="loading-text">正在自动登录...</div>
  15. </div>
  16. </div>
  17. <!-- 主要内容 -->
  18. <div id="app" v-cloak>
  19. <!-- 自动登录状态 -->
  20. <div class="auto-login-container">
  21. <div class="status-icon">
  22. <div v-if="status === 'loading'" class="loading-spinner"></div>
  23. <div v-else-if="status === 'success'" class="success-icon">✓</div>
  24. <div v-else-if="status === 'error'" class="error-icon">✗</div>
  25. </div>
  26. <div class="status-text">{{ statusText }}</div>
  27. <div v-if="status === 'error'" class="retry-section">
  28. <button @click="handleRetry" class="retry-btn">重试</button>
  29. <button @click="handleManualLogin" class="manual-btn">
  30. 手动登录
  31. </button>
  32. </div>
  33. </div>
  34. <!-- Loading 遮罩 -->
  35. <div v-if="showLoading" class="loading-mask">
  36. <div class="loading-content">
  37. <div class="loading-spinner"></div>
  38. <div class="loading-text">{{ loadingText }}</div>
  39. </div>
  40. </div>
  41. <!-- Toast 提示 -->
  42. <div v-if="toast.show" class="toast" :class="toast.type">
  43. {{ toast.message }}
  44. </div>
  45. </div>
  46. <script>
  47. // 获取yhsbToken的函数
  48. function getYhsbToken() {
  49. // 从localStorage获取用户信息
  50. const userInfo = localStorage.getItem("userInfo");
  51. if (userInfo) {
  52. try {
  53. const userData = JSON.parse(userInfo);
  54. return userData.yhsbToken || userData.onlineToken || "";
  55. } catch (e) {
  56. console.error("解析用户信息失败:", e);
  57. }
  58. }
  59. // 从其他可能的存储位置获取
  60. return localStorage.getItem("yhsbToken") || "";
  61. }
  62. // 等待SS框架加载完成
  63. window.SS.ready(function () {
  64. // 使用SS框架的方式创建Vue实例
  65. window.SS.dom.initializeFormApp({
  66. el: "#app",
  67. data() {
  68. return {
  69. status: "loading", // loading, success, error
  70. statusText: "正在自动登录...",
  71. showLoading: false,
  72. loadingText: "",
  73. toast: {
  74. show: false,
  75. message: "",
  76. type: "info",
  77. },
  78. };
  79. },
  80. mounted() {
  81. // 隐藏页面加载状态
  82. const pageLoading = document.getElementById("page-loading");
  83. if (pageLoading) {
  84. pageLoading.style.display = "none";
  85. }
  86. // 初始化页面,使用自定义结果处理
  87. if (typeof initPage === "function") {
  88. initPage("autoLogin", this.handleResult);
  89. }
  90. // 获取URL参数
  91. const urlParams = this.getUrlParams();
  92. console.log("🔗 H5自动登录页面参数:", urlParams);
  93. // 开始自动登录
  94. this.startAutoLogin();
  95. },
  96. methods: {
  97. // 获取URL参数
  98. getUrlParams() {
  99. const params = {};
  100. const urlSearchParams = new URLSearchParams(
  101. window.location.search
  102. );
  103. for (const [key, value] of urlSearchParams) {
  104. params[key] = decodeURIComponent(value);
  105. }
  106. return params;
  107. },
  108. // 处理操作结果(来自小程序的回调)
  109. handleResult(data) {
  110. console.log("🎯 收到小程序回调:", data);
  111. },
  112. // 显示Loading
  113. showLoadingMask(text = "加载中...") {
  114. this.showLoading = true;
  115. this.loadingText = text;
  116. },
  117. // 隐藏Loading
  118. hideLoadingMask() {
  119. this.showLoading = false;
  120. this.loadingText = "";
  121. },
  122. // 开始自动登录
  123. async startAutoLogin() {
  124. try {
  125. this.status = "loading";
  126. this.statusText = "正在自动登录...";
  127. // 从URL参数或localStorage获取yhsbToken进行自动登录
  128. const urlParams = this.getUrlParams();
  129. const yhsbToken = urlParams.yhsbToken || getYhsbToken();
  130. const redirectUrl = urlParams.redirect || "";
  131. const entryScene = urlParams.entryScene || "";
  132. console.log("🔄 开始自动登录,yhsbToken:", yhsbToken);
  133. if (!yhsbToken) {
  134. throw new Error("未找到yhsbToken,无法自动登录");
  135. }
  136. // 使用自动登录接口(参考小程序的实现)
  137. const response = await request.post(
  138. `/service?ssServ=ssLogin&wdConfirmationCaptchaService=0&mdToken=${yhsbToken}`,
  139. { mdToken: yhsbToken },
  140. { loading: false }
  141. );
  142. if (response && response.data) {
  143. console.log(
  144. "✅ 自动登录成功:",
  145. JSON.stringify(response.data)
  146. );
  147. // 构建用户数据(参考登录页面的格式)
  148. const userData = {
  149. devId: response.data.devId,
  150. sbmc: response.data.sbmc,
  151. sessId: response.data.sessId,
  152. xm: response.data.xm,
  153. yhsbToken: response.data.yhsbToken,
  154. onlineToken: response.data.onlineToken,
  155. syList: response.data.sylist,
  156. yhid: response.data.yhid,
  157. yhm: response.data.yhm,
  158. };
  159. // 保存用户信息到H5本地存储(和登录页面一样)
  160. h5UserApi.saveUserInfo(userData);
  161. // 准备返回数据给小程序(和登录页面一样,只是多了isAutoLogin标记)
  162. const loginResult = {
  163. success: true,
  164. userInfo: userData,
  165. isAutoLogin: true, // 标记为自动登录,用于区分返回逻辑
  166. redirectUrl,
  167. entryScene,
  168. };
  169. this.status = "success";
  170. this.statusText = "自动登录成功!";
  171. console.log("✅ 自动登录成功,准备返回数据:", loginResult);
  172. // 延迟返回结果
  173. setTimeout(() => {
  174. this.returnLoginResult(loginResult);
  175. }, 1500);
  176. } else {
  177. throw new Error("自动登录响应数据无效");
  178. }
  179. } catch (error) {
  180. console.error("❌ 自动登录失败:", error);
  181. this.status = "error";
  182. this.statusText = "自动登录失败: " + error.message;
  183. }
  184. },
  185. // 返回登录结果给小程序
  186. returnLoginResult(result) {
  187. console.log("🔄 返回自动登录结果给小程序:", result);
  188. callNative("loginSuccess", "自动登录成功", result);
  189. },
  190. // 重试自动登录
  191. handleRetry() {
  192. console.log("🔄 重试自动登录");
  193. this.startAutoLogin();
  194. },
  195. // 跳转手动登录
  196. handleManualLogin() {
  197. console.log("👤 跳转手动登录");
  198. // 跳转到登录页面
  199. window.location.href =
  200. "/page/login.html" + window.location.search;
  201. },
  202. },
  203. });
  204. console.log("✅ 自动登录页面初始化完成");
  205. });
  206. </script>
  207. <style>
  208. /* 防止Vue模板闪烁 */
  209. [v-cloak] {
  210. display: none !important;
  211. }
  212. /* 页面加载状态 */
  213. .page-loading {
  214. position: fixed;
  215. top: 0;
  216. left: 0;
  217. right: 0;
  218. bottom: 0;
  219. background: #f5f5f5;
  220. display: flex;
  221. align-items: center;
  222. justify-content: center;
  223. z-index: 9999;
  224. }
  225. .page-loading .loading-content {
  226. text-align: center;
  227. }
  228. .page-loading .loading-spinner {
  229. width: 40px;
  230. height: 40px;
  231. border: 4px solid #f3f3f3;
  232. border-top: 4px solid #40ac6d;
  233. border-radius: 50%;
  234. animation: page-spin 1s linear infinite;
  235. margin: 0 auto 15px;
  236. }
  237. @keyframes page-spin {
  238. 0% {
  239. transform: rotate(0deg);
  240. }
  241. 100% {
  242. transform: rotate(360deg);
  243. }
  244. }
  245. .page-loading .loading-text {
  246. color: #666;
  247. font-size: 14px;
  248. }
  249. * {
  250. margin: 0;
  251. padding: 0;
  252. box-sizing: border-box;
  253. }
  254. body {
  255. font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
  256. sans-serif;
  257. background: #f5f5f5;
  258. min-height: 100vh;
  259. display: flex;
  260. align-items: center;
  261. justify-content: center;
  262. }
  263. .auto-login-container {
  264. background: white;
  265. border-radius: 12px;
  266. padding: 40px 30px;
  267. text-align: center;
  268. box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  269. max-width: 300px;
  270. width: 90%;
  271. }
  272. .status-icon {
  273. margin-bottom: 20px;
  274. }
  275. .loading-spinner {
  276. width: 50px;
  277. height: 50px;
  278. border: 4px solid #f3f3f3;
  279. border-top: 4px solid #40ac6d;
  280. border-radius: 50%;
  281. animation: spin 1s linear infinite;
  282. margin: 0 auto;
  283. }
  284. .success-icon {
  285. width: 50px;
  286. height: 50px;
  287. background: #40ac6d;
  288. border-radius: 50%;
  289. display: flex;
  290. align-items: center;
  291. justify-content: center;
  292. color: white;
  293. font-size: 24px;
  294. font-weight: bold;
  295. margin: 0 auto;
  296. }
  297. .error-icon {
  298. width: 50px;
  299. height: 50px;
  300. background: #dc3545;
  301. border-radius: 50%;
  302. display: flex;
  303. align-items: center;
  304. justify-content: center;
  305. color: white;
  306. font-size: 24px;
  307. font-weight: bold;
  308. margin: 0 auto;
  309. }
  310. @keyframes spin {
  311. 0% {
  312. transform: rotate(0deg);
  313. }
  314. 100% {
  315. transform: rotate(360deg);
  316. }
  317. }
  318. .status-text {
  319. font-size: 16px;
  320. color: #333;
  321. margin-bottom: 20px;
  322. line-height: 1.5;
  323. word-break: keep-all;
  324. }
  325. .retry-section {
  326. display: flex;
  327. gap: 10px;
  328. justify-content: center;
  329. }
  330. .retry-btn,
  331. .manual-btn {
  332. padding: 10px 20px;
  333. border: none;
  334. border-radius: 6px;
  335. font-size: 14px;
  336. cursor: pointer;
  337. transition: all 0.3s ease;
  338. }
  339. .retry-btn {
  340. background: #40ac6d;
  341. color: white;
  342. }
  343. .retry-btn:hover {
  344. background: #369a5a;
  345. }
  346. .manual-btn {
  347. background: #6c757d;
  348. color: white;
  349. }
  350. .manual-btn:hover {
  351. background: #5a6268;
  352. }
  353. /* Loading 遮罩 */
  354. .loading-mask {
  355. position: fixed;
  356. top: 0;
  357. left: 0;
  358. right: 0;
  359. bottom: 0;
  360. background: rgba(0, 0, 0, 0.5);
  361. display: flex;
  362. align-items: center;
  363. justify-content: center;
  364. z-index: 10000;
  365. }
  366. .loading-mask .loading-content {
  367. background: white;
  368. padding: 20px;
  369. border-radius: 8px;
  370. text-align: center;
  371. min-width: 120px;
  372. }
  373. .loading-mask .loading-text {
  374. color: #333;
  375. font-size: 14px;
  376. }
  377. /* Toast 提示 */
  378. .toast {
  379. position: fixed;
  380. top: 50%;
  381. left: 50%;
  382. transform: translate(-50%, -50%);
  383. background: rgba(0, 0, 0, 0.8);
  384. color: white;
  385. padding: 12px 20px;
  386. border-radius: 6px;
  387. font-size: 14px;
  388. z-index: 10001;
  389. max-width: 80%;
  390. text-align: center;
  391. }
  392. .toast.success {
  393. background: rgba(40, 167, 69, 0.9);
  394. }
  395. .toast.error {
  396. background: rgba(220, 53, 69, 0.9);
  397. }
  398. </style>
  399. </body>
  400. </html>