img2text.html 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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. <style>
  8. body {
  9. display: flex;
  10. justify-content: center;
  11. align-items: center;
  12. min-height: 100vh;
  13. padding: 20px;
  14. }
  15. .container {
  16. display: grid;
  17. container-type: inline-size;
  18. width: 100%;
  19. max-width: 900px;
  20. gap: 20px;
  21. }
  22. textarea {
  23. width: 100%;
  24. padding: 10px;
  25. min-height: 120px;
  26. }
  27. input[type="file"] {
  28. width: 100%;
  29. padding: 10px;
  30. }
  31. input[type="text"] {
  32. width: 100%;
  33. padding: 10px;
  34. }
  35. button {
  36. width: 100%;
  37. padding: 10px;
  38. }
  39. button:disabled {
  40. opacity: 0.6;
  41. }
  42. .loading {
  43. display: none;
  44. }
  45. .loading.active {
  46. display: block;
  47. }
  48. .spinner {
  49. width: 20px;
  50. height: 20px;
  51. border: 3px solid #f3f3f3;
  52. border-top: 3px solid #000;
  53. border-radius: 50%;
  54. animation: spin 1s linear infinite;
  55. display: inline-block;
  56. }
  57. @keyframes spin {
  58. 0% { transform: rotate(0deg); }
  59. 100% { transform: rotate(360deg); }
  60. }
  61. .result-box {
  62. padding: 20px;
  63. min-height: 200px;
  64. max-height: 500px;
  65. overflow-y: auto;
  66. white-space: pre-wrap;
  67. border: 1px solid #ccc;
  68. }
  69. .image-preview {
  70. max-width: 100%;
  71. max-height: 300px;
  72. margin: 10px 0;
  73. border: 1px solid #ccc;
  74. }
  75. </style>
  76. </head>
  77. <body>
  78. <div class="container">
  79. <textarea id="prompt" placeholder="请输入提示词..."></textarea>
  80. <input type="file" id="imageFile" accept="image/*">
  81. <input type="text" id="imageUrl" placeholder="或输入图片URL">
  82. <img id="imagePreview" class="image-preview" style="display: none;">
  83. <button id="sendBtn" onclick="sendRequest()">发送</button>
  84. <div class="loading" id="loading">
  85. <div class="spinner"></div>
  86. <span>正在处理...</span>
  87. </div>
  88. <div class="result-box" id="resultBox"></div>
  89. </div>
  90. <script>
  91. const apiUrl = 'https://' + window.location.hostname;
  92. const promptTextarea = document.getElementById('prompt');
  93. const imageFileInput = document.getElementById('imageFile');
  94. const imageUrlInput = document.getElementById('imageUrl');
  95. const imagePreview = document.getElementById('imagePreview');
  96. const sendBtn = document.getElementById('sendBtn');
  97. const loadingDiv = document.getElementById('loading');
  98. const resultBox = document.getElementById('resultBox');
  99. let currentImageUrl = '';
  100. imageFileInput.addEventListener('change', (e) => {
  101. const file = e.target.files[0];
  102. if (file) {
  103. const reader = new FileReader();
  104. reader.onload = (event) => {
  105. currentImageUrl = event.target.result;
  106. imagePreview.src = currentImageUrl;
  107. imagePreview.style.display = 'block';
  108. imageUrlInput.value = '';
  109. };
  110. reader.readAsDataURL(file);
  111. }
  112. });
  113. imageUrlInput.addEventListener('input', (e) => {
  114. const url = e.target.value.trim();
  115. if (url) {
  116. currentImageUrl = url;
  117. imagePreview.src = url;
  118. imagePreview.style.display = 'block';
  119. imageFileInput.value = '';
  120. } else {
  121. currentImageUrl = '';
  122. imagePreview.style.display = 'none';
  123. }
  124. });
  125. async function sendRequest() {
  126. const prompt = promptTextarea.value.trim();
  127. if (!prompt) {
  128. alert('请输入提示词!');
  129. return;
  130. }
  131. if (!currentImageUrl) {
  132. alert('请选择图片或输入图片URL!');
  133. return;
  134. }
  135. sendBtn.disabled = true;
  136. loadingDiv.classList.add('active');
  137. resultBox.textContent = '';
  138. try {
  139. const response = await fetch(`${apiUrl}/api/img2text`, {
  140. method: 'POST',
  141. headers: { 'Content-Type': 'application/json' },
  142. body: JSON.stringify({
  143. prompt: prompt,
  144. imageUrl: currentImageUrl
  145. })
  146. });
  147. const data = await response.json();
  148. resultBox.textContent = data.success
  149. ? JSON.stringify(data.data, null, 2)
  150. : `错误: ${data.error}`;
  151. } catch (error) {
  152. resultBox.textContent = `请求失败: ${error.message}`;
  153. } finally {
  154. sendBtn.disabled = false;
  155. loadingDiv.classList.remove('active');
  156. }
  157. }
  158. promptTextarea.addEventListener('keydown', (e) => {
  159. if (e.key === 'Enter' && e.ctrlKey) sendRequest();
  160. });
  161. </script>
  162. </body>
  163. </html>