Index.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. // 全局 Loading 控制器
  2. window.GlobalLoading = (function() {
  3. let overlay = null;
  4. let loadingText = null;
  5. function init() {
  6. overlay = document.getElementById('globalLoadingOverlay');
  7. loadingText = overlay ? overlay.querySelector('.global-loading-text') : null;
  8. }
  9. function show(text = '正在处理...') {
  10. // console.log('[GlobalLoading] show() 被调用');
  11. // console.log('[GlobalLoading] 文本:', text);
  12. if (!overlay) {
  13. // console.log('[GlobalLoading] → 初始化overlay元素...');
  14. init();
  15. }
  16. if (!overlay) {
  17. // console.error('[GlobalLoading] ✗ overlay元素未找到!');
  18. return;
  19. }
  20. // console.log('[GlobalLoading] ✓ overlay元素存在');
  21. if (loadingText) {
  22. loadingText.textContent = text;
  23. // console.log('[GlobalLoading] ✓ 设置Loading文本:', text);
  24. }
  25. overlay.classList.add('is-visible');
  26. // console.log('[GlobalLoading] ✓ 添加is-visible类,Loading应该可见了');
  27. }
  28. function hide() {
  29. // console.log('[GlobalLoading] hide() 被调用');
  30. if (!overlay) {
  31. // console.error('[GlobalLoading] ✗ overlay元素不存在');
  32. return;
  33. }
  34. overlay.classList.remove('is-visible');
  35. // console.log('[GlobalLoading] ✓ 移除is-visible类,Loading已隐藏');
  36. }
  37. return {
  38. show,
  39. hide
  40. };
  41. })();
  42. // 全局 Alert 控制器
  43. window.GlobalAlert = (function() {
  44. let alertContainer = null;
  45. let alertMessage = null;
  46. let hideTimer = null;
  47. function init() {
  48. alertContainer = document.getElementById('globalAlert');
  49. alertMessage = alertContainer ? alertContainer.querySelector('#alertMessage') : null;
  50. }
  51. function show(text, duration = 1500) {
  52. console.log('[GlobalAlert] show() 被调用:', { text, duration });
  53. if (!alertContainer) {
  54. console.log('[GlobalAlert] 初始化 alertContainer');
  55. init();
  56. }
  57. if (!alertContainer) {
  58. console.error('[GlobalAlert] alertContainer 未找到!');
  59. return;
  60. }
  61. if (!alertMessage) {
  62. console.error('[GlobalAlert] alertMessage 未找到!');
  63. return;
  64. }
  65. console.log('[GlobalAlert] 设置消息:', text);
  66. // 清除之前的自动隐藏定时器
  67. if (hideTimer) {
  68. clearTimeout(hideTimer);
  69. hideTimer = null;
  70. }
  71. alertMessage.textContent = text;
  72. alertContainer.classList.add('show');
  73. console.log('[GlobalAlert] 已添加 show 类,alert 应该可见了');
  74. // 自动隐藏
  75. if (duration > 0) {
  76. hideTimer = setTimeout(() => {
  77. hide();
  78. }, duration);
  79. }
  80. }
  81. function hide() {
  82. if (!alertContainer) return;
  83. alertContainer.classList.remove('show');
  84. if (hideTimer) {
  85. clearTimeout(hideTimer);
  86. hideTimer = null;
  87. }
  88. }
  89. return {
  90. show,
  91. hide
  92. };
  93. })();
  94. // 页面管理:负责切换 iframe 中的子页面,并保持与导航栏状态同步
  95. (function () {
  96. console.log('[Index] index.js 已加载');
  97. const DEFAULT_PAGE = "store";
  98. function getPageFrame() {
  99. return document.getElementById("pageFrame");
  100. }
  101. function getNavigationFrame() {
  102. return document.getElementById("navigationFrame");
  103. }
  104. function switchPage(page) {
  105. // 只处理实际的页面切换,不处理login/register
  106. if (page === "login" || page === "register") {
  107. return;
  108. }
  109. // profile页面是独立页面,直接跳转
  110. if (page === "profile") {
  111. window.location.href = "page/profile/profile.html";
  112. return;
  113. }
  114. const frame = getPageFrame();
  115. if (!frame) {
  116. return;
  117. }
  118. switch (page) {
  119. case "store":
  120. frame.src = "page/store/store.html";
  121. break;
  122. case "assets":
  123. frame.src = "page/assets/assets.html";
  124. break;
  125. default:
  126. frame.src = "page/store/store.html";
  127. break;
  128. }
  129. // 同步导航栏状态
  130. syncNavigationState(page);
  131. }
  132. // 同步导航栏状态
  133. function syncNavigationState(page) {
  134. const navigationFrame = getNavigationFrame();
  135. if (navigationFrame && navigationFrame.contentWindow) {
  136. // 使用setTimeout确保iframe已加载
  137. setTimeout(() => {
  138. navigationFrame.contentWindow.postMessage(
  139. { type: "navigation", page },
  140. "*"
  141. );
  142. }, 100);
  143. }
  144. }
  145. window.addEventListener("message", (event) => {
  146. // 调试:记录所有收到的消息
  147. console.log('[Index] 收到message事件:', {
  148. origin: event.origin,
  149. expectedOrigin: window.location.origin,
  150. data: event.data,
  151. source: event.source,
  152. type: event.data?.type
  153. });
  154. // 检查 origin(允许同源或 localhost,或者来自 iframe)
  155. const isSameOrigin = event.origin === window.location.origin;
  156. const isLocalhost = event.origin === 'http://localhost:3000' ||
  157. event.origin === 'http://127.0.0.1:3000' ||
  158. event.origin.startsWith('http://localhost:') ||
  159. event.origin.startsWith('http://127.0.0.1:');
  160. // 允许来自同源的 iframe(即使 origin 不完全匹配)
  161. const isFromIframe = event.source && event.source !== window;
  162. console.log('[Index] Origin 检查:', {
  163. eventOrigin: event.origin,
  164. windowOrigin: window.location.origin,
  165. isSameOrigin,
  166. isLocalhost,
  167. isFromIframe,
  168. willPass: isSameOrigin || isLocalhost || isFromIframe
  169. });
  170. // 对于 global-alert 消息,放宽 origin 检查(允许来自任何同源 iframe)
  171. if (event.data && event.data.type === 'global-alert') {
  172. console.log('[Index] 这是 global-alert 消息,放宽 origin 检查');
  173. // 允许来自任何 iframe 的消息(只要 source 存在且不是 window 本身)
  174. if (isFromIframe || isSameOrigin || isLocalhost) {
  175. console.log('[Index] global-alert 消息通过检查');
  176. } else {
  177. console.warn('[Index] global-alert 消息被 origin 检查过滤:', event.origin);
  178. // 即使 origin 不匹配,也允许 global-alert 消息通过(因为来自同源 iframe)
  179. console.log('[Index] 但允许通过(来自 iframe)');
  180. }
  181. } else if (!isSameOrigin && !isLocalhost && !isFromIframe) {
  182. console.log('[Index] 消息被 origin 检查过滤:', event.origin);
  183. return;
  184. }
  185. console.log('[Index] 消息通过 origin 检查,继续处理');
  186. const { data } = event;
  187. if (data && data.type === "navigation" && (data.page === "login" || data.page === "register")) {
  188. // console.log('[2-Index] 收到login/register消息');
  189. const loginFrame = document.getElementById('loginViewFrame');
  190. if (loginFrame) {
  191. const mode = data.page === "register" ? "register" : "login";
  192. loginFrame.style.display = 'block';
  193. // console.log('[3-Index] iframe已显示');
  194. const sendMode = () => {
  195. if (loginFrame.contentWindow) {
  196. loginFrame.contentWindow.postMessage({
  197. type: 'open-login-view',
  198. mode: mode
  199. }, '*');
  200. // console.log('[4-Index] 消息已发送到login iframe');
  201. } else {
  202. setTimeout(sendMode, 100);
  203. }
  204. };
  205. sendMode();
  206. const handleLoad = () => {
  207. setTimeout(() => {
  208. sendMode();
  209. }, 50);
  210. };
  211. if (loginFrame.contentDocument && loginFrame.contentDocument.readyState === 'complete') {
  212. handleLoad();
  213. } else {
  214. loginFrame.addEventListener('load', handleLoad, { once: true });
  215. }
  216. }
  217. } else if (data && data.type === "navigation" && data.page) {
  218. switchPage(data.page);
  219. }
  220. // 注意:global-alert、global-loading、global-confirm 消息不再通过 index.js 处理
  221. // 各个 view 现在直接调用父窗口的 GlobalAlert/GlobalLoading/GlobalConfirm
  222. else if (data && data.type === "open-export-view") {
  223. // 处理打开导出弹出框
  224. console.log('[Index] 收到open-export-view消息:', data);
  225. if (!data.folderName) {
  226. console.error('[Index] 缺少文件夹名称');
  227. return;
  228. }
  229. if (!window.ExportViewManager) {
  230. console.error('[Index] ExportViewManager 未初始化');
  231. return;
  232. }
  233. // 直接打开弹出框,传递文件夹名称
  234. window.ExportViewManager.show(data.folderName).then((confirmed) => {
  235. console.log('[Index] 用户选择:', confirmed ? '确认导出' : '取消');
  236. // 如果用户确认,可以在这里处理实际的导出下载逻辑
  237. if (confirmed) {
  238. // TODO: 处理实际的导出下载逻辑
  239. console.log('[Index] 用户确认导出,文件夹:', data.folderName);
  240. }
  241. }).catch(error => {
  242. console.error('[Index] ExportViewManager显示失败:', error);
  243. });
  244. } else if (data && data.type === "close-login-view") {
  245. const loginFrame = document.getElementById('loginViewFrame');
  246. if (loginFrame) {
  247. loginFrame.style.display = 'none';
  248. }
  249. } else if (data && data.type === "open-ai-generate-view") {
  250. // 处理打开AI生图界面
  251. console.log('[Index] 收到open-ai-generate-view消息:', data);
  252. const aiGenerateFrame = document.getElementById('aiGenerateViewFrame');
  253. if (aiGenerateFrame) {
  254. aiGenerateFrame.style.display = 'block';
  255. aiGenerateFrame.style.pointerEvents = 'auto';
  256. aiGenerateFrame.style.visibility = 'visible';
  257. const sendAIData = () => {
  258. if (aiGenerateFrame.contentWindow) {
  259. aiGenerateFrame.contentWindow.postMessage({
  260. type: 'show-ai-generate',
  261. folderName: data.folderName,
  262. spritesheetData: data.spritesheetData,
  263. spritesheetLayout: data.spritesheetLayout
  264. }, '*');
  265. } else {
  266. setTimeout(sendAIData, 100);
  267. }
  268. };
  269. sendAIData();
  270. }
  271. } else if (data && data.type === "close-ai-generate-view") {
  272. const aiGenerateFrame = document.getElementById('aiGenerateViewFrame');
  273. if (aiGenerateFrame) {
  274. aiGenerateFrame.style.display = 'none';
  275. aiGenerateFrame.style.pointerEvents = 'none';
  276. aiGenerateFrame.style.visibility = 'hidden';
  277. }
  278. } else if (data && data.type === "open-pay-view") {
  279. // 处理打开支付界面
  280. const payFrame = document.getElementById('payViewFrame');
  281. if (payFrame) {
  282. payFrame.style.display = 'block';
  283. payFrame.style.pointerEvents = 'auto';
  284. payFrame.style.visibility = 'visible';
  285. const sendPayData = () => {
  286. if (payFrame.contentWindow) {
  287. payFrame.contentWindow.postMessage({
  288. type: 'open-pay-view',
  289. itemName: data.itemName,
  290. price: data.price,
  291. resourcePath: data.resourcePath,
  292. categoryDir: data.categoryDir
  293. }, '*');
  294. } else {
  295. setTimeout(sendPayData, 100);
  296. }
  297. };
  298. sendPayData();
  299. const handleLoad = () => {
  300. setTimeout(() => {
  301. sendPayData();
  302. }, 50);
  303. };
  304. if (payFrame.contentDocument && payFrame.contentDocument.readyState === 'complete') {
  305. handleLoad();
  306. } else {
  307. payFrame.addEventListener('load', handleLoad, { once: true });
  308. }
  309. }
  310. } else if (data && data.type === "close-pay-view") {
  311. const payFrame = document.getElementById('payViewFrame');
  312. if (payFrame) {
  313. payFrame.style.display = 'none';
  314. payFrame.style.pointerEvents = 'none';
  315. // 确保 iframe 不会遮挡其他元素
  316. payFrame.style.visibility = 'hidden';
  317. }
  318. } else if (data && data.type === "payment-success") {
  319. // 支付成功,刷新商店页面(如果需要)
  320. if (window.HintView) {
  321. window.HintView.success(`购买成功!${data.itemName} 已添加到网盘`, 3000);
  322. }
  323. } else if (data && data.type === "open-recharge-view") {
  324. // 处理打开充值界面
  325. const rechargeFrame = document.getElementById('rechargeViewFrame');
  326. if (rechargeFrame) {
  327. rechargeFrame.style.display = 'block';
  328. rechargeFrame.style.pointerEvents = 'auto';
  329. rechargeFrame.style.visibility = 'visible';
  330. const sendRechargeData = () => {
  331. if (rechargeFrame.contentWindow) {
  332. rechargeFrame.contentWindow.postMessage({
  333. type: 'open-recharge-view',
  334. needPoints: data.needPoints,
  335. currentPoints: data.currentPoints
  336. }, '*');
  337. } else {
  338. setTimeout(sendRechargeData, 100);
  339. }
  340. };
  341. sendRechargeData();
  342. const handleLoad = () => {
  343. setTimeout(() => {
  344. sendRechargeData();
  345. }, 50);
  346. };
  347. if (rechargeFrame.contentDocument && rechargeFrame.contentDocument.readyState === 'complete') {
  348. handleLoad();
  349. } else {
  350. rechargeFrame.addEventListener('load', handleLoad, { once: true });
  351. }
  352. }
  353. } else if (data && data.type === "close-recharge-view") {
  354. const rechargeFrame = document.getElementById('rechargeViewFrame');
  355. if (rechargeFrame) {
  356. rechargeFrame.style.display = 'none';
  357. rechargeFrame.style.pointerEvents = 'none';
  358. rechargeFrame.style.visibility = 'hidden';
  359. }
  360. } else if (data && data.type === "recharge-success") {
  361. // 充值成功,关闭充值界面
  362. const rechargeFrame = document.getElementById('rechargeViewFrame');
  363. if (rechargeFrame) {
  364. rechargeFrame.style.display = 'none';
  365. rechargeFrame.style.pointerEvents = 'none';
  366. rechargeFrame.style.visibility = 'hidden';
  367. }
  368. // 刷新点数显示
  369. if (window.postMessage) {
  370. window.postMessage({ type: 'refresh-points' }, '*');
  371. }
  372. // 显示成功提示
  373. if (window.HintView) {
  374. window.HintView.success(`充值成功!获得 ${data.points} Ani币`, 3000);
  375. }
  376. } else if (data && data.type === "avatar-updated") {
  377. // 头像更新,通知导航栏更新
  378. const navigationFrame = document.getElementById('navigationFrame');
  379. if (navigationFrame && navigationFrame.contentWindow) {
  380. // 从localStorage获取用户信息并更新头像
  381. try {
  382. const loginDataStr = localStorage.getItem('loginData');
  383. if (loginDataStr) {
  384. const loginData = JSON.parse(loginDataStr);
  385. if (loginData.user) {
  386. loginData.user.avatar = data.avatar;
  387. localStorage.setItem('loginData', JSON.stringify(loginData));
  388. // 通知导航栏更新
  389. navigationFrame.contentWindow.postMessage({
  390. type: 'login-success',
  391. user: loginData.user
  392. }, '*');
  393. }
  394. }
  395. } catch (error) {
  396. console.error('[Index] 更新头像信息失败:', error);
  397. }
  398. }
  399. } else if (data && data.type === "login-success" && data.user) {
  400. // 显示登录成功提示(在主窗口)
  401. if (window.HintView) {
  402. window.HintView.success('登录成功', 2000);
  403. }
  404. // 处理登录成功消息,转发给 navigation iframe 和 pageFrame
  405. const navigationFrame = getNavigationFrame();
  406. if (navigationFrame && navigationFrame.contentWindow) {
  407. navigationFrame.contentWindow.postMessage({
  408. type: 'login-success',
  409. user: data.user
  410. }, '*');
  411. }
  412. // 也转发给 pageFrame(store 页面)
  413. const pageFrame = getPageFrame();
  414. if (pageFrame && pageFrame.contentWindow) {
  415. pageFrame.contentWindow.postMessage({
  416. type: 'login-success',
  417. user: data.user
  418. }, '*');
  419. }
  420. // 转发给 assets iframe(如果存在),它会再转发给 disk iframe
  421. const assetsFrame = document.getElementById('assetsFrame');
  422. if (assetsFrame && assetsFrame.contentWindow) {
  423. assetsFrame.contentWindow.postMessage({
  424. type: 'login-success',
  425. user: data.user
  426. }, '*');
  427. }
  428. } else if (data && data.type === "refresh-points") {
  429. // 刷新用户点数显示
  430. console.log('[Index] 收到刷新点数请求');
  431. // 转发给导航栏更新点数
  432. const navigationFrame = getNavigationFrame();
  433. if (navigationFrame && navigationFrame.contentWindow) {
  434. navigationFrame.contentWindow.postMessage({ type: 'refresh-points' }, '*');
  435. }
  436. // 也转发给个人中心页面(如果打开的话)
  437. const pageFrame = getPageFrame();
  438. if (pageFrame && pageFrame.contentWindow) {
  439. pageFrame.contentWindow.postMessage({ type: 'refresh-points' }, '*');
  440. }
  441. } else if (data && data.type === "logout") {
  442. // 清除 localStorage 中的登录信息
  443. try {
  444. localStorage.removeItem('loginData');
  445. console.log('[Index] 登出,已清除登录信息');
  446. } catch (error) {
  447. console.error('[Index] 清除登录信息失败:', error);
  448. }
  449. // 处理登出消息,转发给所有 iframe
  450. const navigationFrame = getNavigationFrame();
  451. if (navigationFrame && navigationFrame.contentWindow) {
  452. navigationFrame.contentWindow.postMessage({
  453. type: 'logout'
  454. }, '*');
  455. }
  456. const pageFrame = getPageFrame();
  457. if (pageFrame && pageFrame.contentWindow) {
  458. pageFrame.contentWindow.postMessage({
  459. type: 'logout'
  460. }, '*');
  461. }
  462. const assetsFrame = document.getElementById('assetsFrame');
  463. if (assetsFrame && assetsFrame.contentWindow) {
  464. assetsFrame.contentWindow.postMessage({
  465. type: 'logout'
  466. }, '*');
  467. }
  468. }
  469. });
  470. // 检查并恢复登录状态
  471. function checkAndRestoreLogin() {
  472. try {
  473. const loginDataStr = localStorage.getItem('loginData');
  474. if (!loginDataStr) {
  475. return;
  476. }
  477. const loginData = JSON.parse(loginDataStr);
  478. const now = Date.now();
  479. // 检查是否过期(2小时)
  480. if (now >= loginData.expireTime) {
  481. // 已过期,清除登录信息
  482. localStorage.removeItem('loginData');
  483. console.log('[Index] 登录信息已过期(2小时),已清除');
  484. return;
  485. }
  486. // 未过期,恢复登录状态
  487. if (loginData.user) {
  488. console.log('[Index] 恢复登录状态,用户:', loginData.user.username);
  489. // 通知所有 iframe 登录成功
  490. const navigationFrame = getNavigationFrame();
  491. if (navigationFrame && navigationFrame.contentWindow) {
  492. navigationFrame.contentWindow.postMessage({
  493. type: 'login-success',
  494. user: loginData.user
  495. }, '*');
  496. }
  497. const pageFrame = getPageFrame();
  498. if (pageFrame && pageFrame.contentWindow) {
  499. pageFrame.contentWindow.postMessage({
  500. type: 'login-success',
  501. user: loginData.user
  502. }, '*');
  503. }
  504. const assetsFrame = document.getElementById('assetsFrame');
  505. if (assetsFrame && assetsFrame.contentWindow) {
  506. assetsFrame.contentWindow.postMessage({
  507. type: 'login-success',
  508. user: loginData.user
  509. }, '*');
  510. }
  511. }
  512. } catch (error) {
  513. console.error('[Index] 恢复登录状态失败:', error);
  514. localStorage.removeItem('loginData');
  515. }
  516. }
  517. window.addEventListener("DOMContentLoaded", () => {
  518. // 先检查并恢复登录状态
  519. checkAndRestoreLogin();
  520. switchPage(DEFAULT_PAGE);
  521. syncNavigationState(DEFAULT_PAGE);
  522. });
  523. })();