signup.html 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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, user-scalable=no">
  6. <title>活动报名</title>
  7. <style>
  8. body { margin: 0; padding: 0; font-family: -apple-system, sans-serif; background-color: white; padding-bottom: 80px; }
  9. .hero { width: 100%; height: 240px; background: #ddd url('https://via.placeholder.com/400x240/34c759/ffffff?text=Running') center/cover; position: relative; }
  10. .back-btn { position: absolute; top: 40px; left: 20px; width: 36px; height: 36px; background: rgba(0,0,0,0.4); border-radius: 50%; color: white; display: flex; align-items: center; justify-content: center; font-size: 20px; cursor: pointer; backdrop-filter: blur(4px); }
  11. .container { padding: 20px; }
  12. .title { font-size: 26px; font-weight: bold; margin-bottom: 10px; color: #333; }
  13. .info-row { display: flex; margin-bottom: 15px; align-items: center; color: #666; font-size: 14px; }
  14. .icon { margin-right: 8px; }
  15. .desc-box { margin-top: 30px; border-top: 1px solid #eee; padding-top: 20px; }
  16. .desc-title { font-weight: bold; font-size: 18px; margin-bottom: 10px; }
  17. .desc-text { line-height: 1.6; color: #666; font-size: 15px; }
  18. .bottom-bar { position: fixed; bottom: 0; left: 0; width: 100%; background: white; border-top: 1px solid #eee; padding: 10px 20px; box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; }
  19. .status-text { font-size: 12px; color: #999; }
  20. .btn { width: 100%; padding: 14px; border-radius: 30px; text-align: center; color: white; font-weight: bold; font-size: 18px; cursor: pointer; border: none; transition: background 0.3s; }
  21. .btn-blue { background: #007aff; box-shadow: 0 4px 12px rgba(0,122,255,0.3); }
  22. .btn-blue:active { background: #005bb5; }
  23. .btn-green { background: #34c759; box-shadow: 0 4px 12px rgba(52,199,89,0.3); }
  24. .btn-green:active { background: #248a3d; }
  25. .btn-disabled { background: #ccc; cursor: not-allowed; box-shadow: none; }
  26. </style>
  27. <script src="./mock_flutter.js"></script>
  28. <script src="./bridge.js"></script>
  29. <script src="./api.js"></script>
  30. </head>
  31. <body>
  32. <div class="hero">
  33. <div class="back-btn" onclick="Bridge.back()">←</div>
  34. </div>
  35. <div class="container">
  36. <div class="title" id="eventName">加载中...</div>
  37. <div class="info-row">
  38. <span class="icon">🕒</span>
  39. <span id="eventTime">--</span>
  40. </div>
  41. <div class="info-row">
  42. <span class="icon">📍</span>
  43. <span>全国线上活动</span>
  44. </div>
  45. <div class="desc-box">
  46. <div class="desc-title">活动简介</div>
  47. <div class="desc-text">
  48. 这是一场激动人心的线上挑战赛!无论你在哪里,只要迈开双腿,就能参与其中。<br><br>
  49. <b>参赛规则:</b><br>
  50. 1. 点击下方按钮报名。<br>
  51. 2. 使用 App 记录运动轨迹。<br>
  52. 3. 完赛后获得电子证书。
  53. </div>
  54. </div>
  55. </div>
  56. <div class="bottom-bar">
  57. <button id="actionBtn" class="btn btn-disabled" onclick="handleAction()">加载状态...</button>
  58. </div>
  59. <script>
  60. var TOKEN = '96ba3c924394934f7d30fa869a94ce0d';
  61. var EC_ID = 0; // 卡片ID
  62. var MC_ID = 101; // 赛事ID (用于报名)
  63. var state = {
  64. isJoin: false,
  65. isLoading: true
  66. };
  67. // 检测是否本地开发环境
  68. var isLocal = window.location.protocol === 'file:' ||
  69. window.location.hostname === 'localhost' ||
  70. window.location.hostname === '127.0.0.1';
  71. API.init({
  72. token: TOKEN,
  73. useMock: isLocal // 本地开发自动开启 Mock
  74. });
  75. // 初始化页面
  76. initPage();
  77. function initPage() {
  78. // 1. 获取活动详情
  79. API.getCardDetail(EC_ID).then(function(data) {
  80. document.getElementById('eventName').innerText = data.mcName || '演示活动:线上马拉松';
  81. // 简单格式化时间,真实项目建议用 momentjs 或 tools
  82. document.getElementById('eventTime').innerText = '活动时间: 近期有效';
  83. }).catch(function(err) {
  84. console.warn('详情加载失败(可能用mock数据)', err);
  85. document.getElementById('eventName').innerText = '演示活动:线上马拉松';
  86. });
  87. // 2. 检查报名状态
  88. checkJoinStatus();
  89. }
  90. function checkJoinStatus() {
  91. setBtnLoading(true);
  92. API.getUserJoinStatus(EC_ID).then(function(data) {
  93. state.isJoin = data.isJoin;
  94. updateBtnState();
  95. }).catch(function(err) {
  96. console.error(err);
  97. alert('获取报名状态失败');
  98. // 默认未报名
  99. state.isJoin = false;
  100. updateBtnState();
  101. });
  102. }
  103. function updateBtnState() {
  104. var btn = document.getElementById('actionBtn');
  105. state.isLoading = false;
  106. btn.className = state.isJoin ? 'btn btn-green' : 'btn btn-blue';
  107. btn.innerText = state.isJoin ? '已报名,进入比赛' : '立即报名';
  108. }
  109. function setBtnLoading(loading) {
  110. state.isLoading = loading;
  111. var btn = document.getElementById('actionBtn');
  112. if (loading) {
  113. btn.className = 'btn btn-disabled';
  114. btn.innerText = '处理中...';
  115. }
  116. }
  117. function handleAction() {
  118. if (state.isLoading) return;
  119. if (state.isJoin) {
  120. // 已报名 -> 进入比赛 (调用 Bridge 打开原生页面)
  121. Bridge.openMatch(MC_ID, 1); // type=1 普通活动
  122. } else {
  123. // 未报名 -> 执行报名
  124. doSignUp();
  125. }
  126. }
  127. function doSignUp() {
  128. if (!confirm('确定要报名参加这个活动吗?')) return;
  129. setBtnLoading(true);
  130. API.signUpOnline(MC_ID).then(function(res) {
  131. alert('🎉 报名成功!');
  132. // 刷新状态
  133. checkJoinStatus();
  134. }).catch(function(err) {
  135. setBtnLoading(false);
  136. console.error(err);
  137. // Mock 模式下 API 可能会失败(没有真实后端),我们模拟成功
  138. if (window.location.protocol === 'file:') {
  139. alert('🎉 报名成功!(Mock模式)');
  140. state.isJoin = true;
  141. updateBtnState();
  142. } else {
  143. alert('报名失败: ' + err.message);
  144. }
  145. });
  146. }
  147. </script>
  148. </body>
  149. </html>