announcement.html 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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, maximum-scale=1.0, user-scalable=no">
  6. <title>公告详情</title>
  7. <link rel="stylesheet" href="/css/activity.css">
  8. <style>
  9. .activity-content {
  10. min-height: 100vh;
  11. }
  12. .activity-content img {
  13. max-width: 100%;
  14. height: auto;
  15. }
  16. .activity-content a {
  17. color: #f5a623;
  18. }
  19. </style>
  20. </head>
  21. <body>
  22. <div class="container activity">
  23. <div id="activityContent" class="activity-content">
  24. </div>
  25. <div class="loading-overlay" id="loadingOverlay" style="display: none;">
  26. <div class="loading-content">
  27. <div class="loading-spinner"></div>
  28. <span class="loading-text">加载中...</span>
  29. </div>
  30. </div>
  31. <div class="error-overlay" id="errorOverlay" style="display: none;">
  32. <div class="error-content">
  33. <div class="error-icon">!</div>
  34. <span class="error-text">加载失败,请稍后重试</span>
  35. <button class="error-retry" id="retryBtn">重试</button>
  36. </div>
  37. </div>
  38. </div>
  39. <script src="/js/app.js"></script>
  40. <script>
  41. (function () {
  42. const API_URL = '/api/v1/config/announcement/';
  43. const loadingOverlay = document.getElementById('loadingOverlay');
  44. const errorOverlay = document.getElementById('errorOverlay');
  45. const retryBtn = document.getElementById('retryBtn');
  46. // const backButton = document.getElementById('backButton');
  47. const activityContent = document.getElementById('activityContent');
  48. // const activityTitle = document.getElementById('activityTitle');
  49. let token = '';
  50. let activityId = '';
  51. // backButton.addEventListener('click', function () {
  52. // if (window.flutter_inappwebview && window.flutter_inappwebview.callHandler) {
  53. // window.flutter_inappwebview.callHandler('closeWebview');
  54. // }
  55. // });
  56. function showLoading() {
  57. loadingOverlay.style.display = 'flex';
  58. errorOverlay.style.display = 'none';
  59. }
  60. function hideLoading() {
  61. loadingOverlay.style.display = 'none';
  62. }
  63. function showError() {
  64. loadingOverlay.style.display = 'none';
  65. errorOverlay.style.display = 'flex';
  66. }
  67. async function getTokenFromNative() {
  68. try {
  69. const result = await window.flutter_inappwebview.callHandler('getToken');
  70. token = result;
  71. return result;
  72. } catch (e) {
  73. console.error('JSBridge getToken 调用失败', e);
  74. return null;
  75. }
  76. }
  77. async function getActivityIdFromNative() {
  78. try {
  79. const result = await window.flutter_inappwebview.callHandler('getActivityId');
  80. activityId = result;
  81. return result;
  82. } catch (e) {
  83. console.error('JSBridge getActivityId 调用失败', e);
  84. return null;
  85. }
  86. }
  87. async function fetchActivityDetail() {
  88. showLoading();
  89. try {
  90. const headers = {
  91. 'Content-Type': 'application/json',
  92. 'Accept': 'application/json',
  93. 'X-Requested-With': 'XMLHttpRequest',
  94. ...globalHeaders
  95. };
  96. if (token) {
  97. headers['Authorization'] = token;
  98. }
  99. const response = await fetch(API_URL + activityId, {
  100. method: 'GET',
  101. headers: headers,
  102. credentials: 'include',
  103. timeout: 10000
  104. });
  105. if (!response.ok) {
  106. throw new Error(`请求失败! status: ${response.status}`);
  107. }
  108. const result = await response.json();
  109. const data = result.data || {};
  110. if (data.content) {
  111. const decodedHTML = decodeHTMLEntities(data.content);
  112. activityContent.innerHTML = decodedHTML;
  113. } else {
  114. activityContent.innerHTML = '<p style="color: #888; text-align: center; padding: 20px;">暂无内容</p>';
  115. }
  116. hideLoading();
  117. } catch (error) {
  118. console.error('Data fetch error:', error);
  119. showError();
  120. }
  121. }
  122. retryBtn.addEventListener('click', fetchActivityDetail);
  123. document.addEventListener('DOMContentLoaded', async function () {
  124. await getTokenFromNative();
  125. await getActivityIdFromNative();
  126. if (activityId) {
  127. fetchActivityDetail();
  128. } else {
  129. console.error('未获取到 activityId');
  130. showError();
  131. }
  132. });
  133. })();
  134. function decodeHTMLEntities(text) {
  135. const textarea = document.createElement('textarea');
  136. textarea.innerHTML = text;
  137. return textarea.value;
  138. }
  139. </script>
  140. </body>
  141. </html>