ModeSelector.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. /**
  2. * 処理モード選択コンポーネント
  3. * ファイルアップロード時に高速モードまたは精密モードを選択するために使用
  4. */
  5. import React, { useState, useEffect } from 'react';
  6. import { uploadService } from '../services/uploadService';
  7. import { ModeRecommendation } from '../types';
  8. interface ModeSelectorProps {
  9. file: File | null;
  10. onModeChange: (mode: 'fast' | 'precise') => void;
  11. className?: string;
  12. }
  13. export const ModeSelector: React.FC<ModeSelectorProps> = ({
  14. file,
  15. onModeChange,
  16. className = '',
  17. }) => {
  18. const [selectedMode, setSelectedMode] = useState<'fast' | 'precise'>('fast');
  19. const [recommendation, setRecommendation] = useState<ModeRecommendation | null>(null);
  20. const [loading, setLoading] = useState(false);
  21. useEffect(() => {
  22. if (file) {
  23. loadRecommendation();
  24. } else {
  25. setRecommendation(null);
  26. }
  27. }, [file]);
  28. const loadRecommendation = async () => {
  29. if (!file) return;
  30. setLoading(true);
  31. try {
  32. const rec = await uploadService.recommendMode(file);
  33. setRecommendation(rec);
  34. // 推薦されたモードを自動選択
  35. setSelectedMode(rec.recommendedMode);
  36. onModeChange(rec.recommendedMode);
  37. } catch (error) {
  38. console.error('モード推奨の取得に失敗しました:', error);
  39. } finally {
  40. setLoading(false);
  41. }
  42. };
  43. const handleModeChange = (mode: 'fast' | 'precise') => {
  44. setSelectedMode(mode);
  45. onModeChange(mode);
  46. };
  47. if (!file) {
  48. return null;
  49. }
  50. return (
  51. <div className={`mode-selector ${className}`}>
  52. <div className="mode-selector-header">
  53. <h4>処理モードの選択</h4>
  54. {loading && <span className="loading">分析中...</span>}
  55. </div>
  56. {/* 模式推荐信息 */}
  57. {recommendation && (
  58. <div className="recommendation-info">
  59. <div className="reason">
  60. <strong>推奨:</strong> {recommendation.reason}
  61. </div>
  62. {recommendation.warnings && recommendation.warnings.length > 0 && (
  63. <div className="warnings">
  64. {recommendation.warnings.map((warning, idx) => (
  65. <div key={idx} className="warning-item">
  66. ⚠️ {warning}
  67. </div>
  68. ))}
  69. </div>
  70. )}
  71. </div>
  72. )}
  73. {/* 模式选择 */}
  74. <div className="mode-options">
  75. <label className={`mode-option ${selectedMode === 'fast' ? 'selected' : ''}`}>
  76. <input
  77. type="radio"
  78. name="processing-mode"
  79. value="fast"
  80. checked={selectedMode === 'fast'}
  81. onChange={() => handleModeChange('fast')}
  82. />
  83. <div className="mode-content">
  84. <div className="mode-title">⚡ 高速モード</div>
  85. <div className="mode-desc">
  86. テキストを単純に抽出、高速、プレーンテキストドキュメントに最適
  87. </div>
  88. <div className="mode-benefits">
  89. ✅ 高速<br />
  90. ✅ 追加コストなし<br />
  91. ❌ テキスト情報のみ処理
  92. </div>
  93. </div>
  94. </label>
  95. <label className={`mode-option ${selectedMode === 'precise' ? 'selected' : ''}`}>
  96. <input
  97. type="radio"
  98. name="processing-mode"
  99. value="precise"
  100. checked={selectedMode === 'precise'}
  101. onChange={() => handleModeChange('precise')}
  102. />
  103. <div className="mode-content">
  104. <div className="mode-title">🎯 精密モード</div>
  105. <div className="mode-desc">
  106. 内容を正確に認識し、完全な情報を保持
  107. </div>
  108. <div className="mode-benefits">
  109. ✅ 画像/表を認識<br />
  110. ✅ レイアウト情報を保持<br />
  111. ✅ 図文混合コンテンツ<br />
  112. ⚠️ API費用が必要<br />
  113. ⚠️ 処理時間が長い
  114. </div>
  115. </div>
  116. </label>
  117. </div>
  118. <style jsx>{`
  119. .mode-selector {
  120. margin: 16px 0;
  121. padding: 16px;
  122. border: 1px solid #e0e0e0;
  123. border-radius: 8px;
  124. background: #fafafa;
  125. }
  126. .mode-selector-header {
  127. display: flex;
  128. justify-content: space-between;
  129. align-items: center;
  130. margin-bottom: 12px;
  131. }
  132. .mode-selector-header h4 {
  133. margin: 0;
  134. font-size: 16px;
  135. font-weight: 600;
  136. }
  137. .loading {
  138. color: #1890ff;
  139. font-size: 12px;
  140. }
  141. .recommendation-info {
  142. margin-bottom: 16px;
  143. padding: 12px;
  144. background: #e6f7ff;
  145. border: 1px solid #91d5ff;
  146. border-radius: 6px;
  147. font-size: 13px;
  148. }
  149. .recommendation-info .reason {
  150. margin-bottom: 8px;
  151. color: #0050b3;
  152. }
  153. .warnings {
  154. margin-top: 8px;
  155. padding-top: 8px;
  156. border-top: 1px dashed #91d5ff;
  157. }
  158. .warning-item {
  159. color: #d4380d;
  160. margin: 4px 0;
  161. }
  162. .mode-options {
  163. display: grid;
  164. grid-template-columns: 1fr 1fr;
  165. gap: 12px;
  166. }
  167. .mode-option {
  168. display: flex;
  169. gap: 8px;
  170. padding: 12px;
  171. border: 2px solid #d9d9d9;
  172. border-radius: 8px;
  173. cursor: pointer;
  174. transition: all 0.2s;
  175. background: white;
  176. }
  177. .mode-option:hover {
  178. border-color: #1890ff;
  179. background: #f0f7ff;
  180. }
  181. .mode-option.selected {
  182. border-color: #1890ff;
  183. background: #e6f7ff;
  184. box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
  185. }
  186. .mode-option input[type="radio"] {
  187. margin-top: 4px;
  188. cursor: pointer;
  189. }
  190. .mode-content {
  191. flex: 1;
  192. }
  193. .mode-title {
  194. font-weight: 600;
  195. font-size: 14px;
  196. margin-bottom: 4px;
  197. }
  198. .mode-desc {
  199. font-size: 12px;
  200. color: #666;
  201. margin-bottom: 8px;
  202. line-height: 1.4;
  203. }
  204. .mode-benefits {
  205. font-size: 11px;
  206. line-height: 1.6;
  207. color: #555;
  208. }
  209. @media (max-width: 768px) {
  210. .mode-options {
  211. grid-template-columns: 1fr;
  212. }
  213. }
  214. `}</style>
  215. </div>
  216. );
  217. };