settings.html 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <title>设置</title>
  6. <style>
  7. body { font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; margin: 16px; }
  8. label { display: block; margin-bottom: 4px; font-size: 13px; color: #555; }
  9. input { width: 100%; box-sizing: border-box; padding: 6px 8px; margin-bottom: 12px;
  10. border: 1px solid #ccc; border-radius: 6px; font-size: 13px; }
  11. button { width: 100%; padding: 8px; border: none; border-radius: 6px;
  12. background: #4A9BD7; color: #fff; font-size: 14px; cursor: pointer; }
  13. button.secondary { background: #9aa4ad; margin-top: 8px; }
  14. button:disabled { opacity: .6; cursor: default; }
  15. .hint { font-size: 12px; color: #999; margin: -8px 0 8px; }
  16. #mascot-preview { width: 72px; height: 72px; object-fit: contain;
  17. border: 1px dashed #ccc; border-radius: 8px; display: none; margin-bottom: 8px; }
  18. hr { border: none; border-top: 1px solid #eee; margin: 14px 0; }
  19. #status { margin-top: 10px; font-size: 12px; color: #999; }
  20. #wechat-qr { display: none; width: 180px; height: 180px; margin: 8px auto 0; }
  21. </style>
  22. </head>
  23. <body>
  24. <label for="base-url">后端地址</label>
  25. <input id="base-url" placeholder="https://cfc.cf-club.com" />
  26. <button id="save">保存</button>
  27. <hr />
  28. <label>宠物形象</label>
  29. <img id="mascot-preview" alt="preview" />
  30. <input id="mascot-file" type="file" accept="image/png,image/jpeg,image/webp" />
  31. <div class="hint">支持 PNG / JPG / WebP,建议正方形、≤2MB</div>
  32. <button id="upload-mascot" disabled>上传并使用此形象</button>
  33. <button id="clear-mascot" class="secondary">恢复默认形象</button>
  34. <hr />
  35. <label>微信登录</label>
  36. <button id="wechat-login">微信扫码登录</button>
  37. <div class="hint">点击后出现小程序码,用微信扫码并在小程序内确认,即可自动与小程序账号同步登录(无需手动填令牌)</div>
  38. <img id="wechat-qr" alt="小程序码" />
  39. <div id="status"></div>
  40. <script>
  41. (function () {
  42. 'use strict';
  43. var invoke = window.__TAURI__.core.invoke;
  44. var baseUrlEl = document.getElementById('base-url');
  45. var statusEl = document.getElementById('status');
  46. var fileEl = document.getElementById('mascot-file');
  47. var previewEl = document.getElementById('mascot-preview');
  48. var uploadBtn = document.getElementById('upload-mascot');
  49. var clearBtn = document.getElementById('clear-mascot');
  50. function setStatus(msg) { statusEl.textContent = msg || ''; }
  51. invoke('get_settings').then(function (s) {
  52. baseUrlEl.value = s.baseUrl || 'https://cfc.cf-club.com';
  53. if (s.customMascotUrl) {
  54. previewEl.src = s.customMascotUrl;
  55. previewEl.style.display = 'block';
  56. }
  57. setStatus('');
  58. }).catch(function () {});
  59. document.getElementById('save').addEventListener('click', function () {
  60. var baseUrl = baseUrlEl.value.trim();
  61. invoke('save_settings', { baseUrl: baseUrl })
  62. .then(function () {
  63. setStatus('已保存,轮询已重启');
  64. setTimeout(function () { setStatus(''); }, 2500);
  65. })
  66. .catch(function (e) { setStatus('保存失败:' + e); });
  67. });
  68. fileEl.addEventListener('change', function () {
  69. var file = fileEl.files && fileEl.files[0];
  70. uploadBtn.disabled = !file;
  71. if (!file) { previewEl.style.display = 'none'; return; }
  72. var reader = new FileReader();
  73. reader.onload = function () {
  74. previewEl.src = reader.result;
  75. previewEl.style.display = 'block';
  76. };
  77. reader.readAsDataURL(file);
  78. });
  79. uploadBtn.addEventListener('click', function () {
  80. var file = fileEl.files && fileEl.files[0];
  81. if (!file) return;
  82. if (file.size > 10 * 1024 * 1024) { setStatus('图片过大(上限 10MB)'); return; }
  83. uploadBtn.disabled = true;
  84. setStatus('上传中…');
  85. var reader = new FileReader();
  86. reader.onload = function () {
  87. var result = String(reader.result);
  88. var comma = result.indexOf(',');
  89. var meta = result.substring(0, comma);
  90. var base64 = result.substring(comma + 1);
  91. var mime = meta.indexOf(':') >= 0 && meta.indexOf(';') > 0
  92. ? meta.substring(meta.indexOf(':') + 1, meta.indexOf(';')) : '';
  93. invoke('upload_mascot', {
  94. filename: file.name,
  95. mime: mime,
  96. dataBase64: base64
  97. }).then(function () {
  98. setStatus('已上传并使用该形象');
  99. uploadBtn.disabled = false;
  100. setTimeout(function () { setStatus(''); }, 3000);
  101. }).catch(function (e) {
  102. setStatus('上传失败:' + e);
  103. uploadBtn.disabled = false;
  104. });
  105. };
  106. reader.onerror = function () { setStatus('读取图片失败'); uploadBtn.disabled = false; };
  107. reader.readAsDataURL(file);
  108. });
  109. clearBtn.addEventListener('click', function () {
  110. invoke('clear_custom_mascot').then(function () {
  111. fileEl.value = '';
  112. previewEl.style.display = 'none';
  113. uploadBtn.disabled = true;
  114. setStatus('已恢复默认形象');
  115. setTimeout(function () { setStatus(''); }, 2500);
  116. }).catch(function (e) { setStatus('操作失败:' + e); });
  117. });
  118. document.getElementById('wechat-login').addEventListener('click', function () {
  119. setStatus('正在获取小程序码…');
  120. invoke('start_wechat_login').then(function () {
  121. setStatus('请用微信扫码并在小程序内确认');
  122. }).catch(function (e) { setStatus('发起登录失败:' + e); });
  123. });
  124. // Rust 获取到小程序码后由 emit 推送
  125. try {
  126. window.__TAURI__.event.listen('pet://wechat-qr', function (ev) {
  127. var p = (ev && ev.payload) || {};
  128. var img = document.getElementById('wechat-qr');
  129. if (p.qrImage) {
  130. img.src = p.qrImage;
  131. img.style.display = 'block';
  132. setStatus('请用微信扫码并在小程序内确认');
  133. } else {
  134. img.style.display = 'none';
  135. setStatus('(测试模式)已生成登录状态:请在已登录的小程序内访问扫码登录接口确认');
  136. }
  137. });
  138. } catch (e) { /* event API 不可用时忽略 */ }
  139. // 后台轮询任务完成后由 Rust 侧 emit
  140. try {
  141. window.__TAURI__.event.listen('pet://wechat-login', function (ev) {
  142. var p = (ev && ev.payload) || {};
  143. if (p.status === 'success') {
  144. setStatus('登录成功' + (p.nickname ? ':' + p.nickname : ''));
  145. document.getElementById('wechat-qr').style.display = 'none';
  146. } else if (p.status === 'pending') {
  147. setStatus('等待扫码…');
  148. } else if (p.status === 'timeout') {
  149. setStatus('登录超时,请重试');
  150. document.getElementById('wechat-qr').style.display = 'none';
  151. } else if (p.status === 'notfound') {
  152. setStatus('该微信未绑定小程序账号');
  153. } else if (p.status === 'expired') {
  154. setStatus('二维码已过期,请重试');
  155. document.getElementById('wechat-qr').style.display = 'none';
  156. } else {
  157. setStatus('登录失败:' + (p.message || p.status));
  158. }
  159. });
  160. } catch (e) { /* event API 不可用时忽略 */ }
  161. })();
  162. </script>
  163. </body>
  164. </html>