diff --git a/gui/src-frontend/src/App.tsx b/gui/src-frontend/src/App.tsx index 21bb161..f8c293b 100644 --- a/gui/src-frontend/src/App.tsx +++ b/gui/src-frontend/src/App.tsx @@ -5,6 +5,11 @@ import ExportPanel from './components/ExportPanel'; import HistoryList from './components/HistoryList'; import TextMode from './modes/TextMode'; import UrlMode from './modes/UrlMode'; +import WifiMode from './modes/WifiMode'; +import VCardMode from './modes/VCardMode'; +import EmailMode from './modes/EmailMode'; +import PhoneMode from './modes/PhoneMode'; +import SmsMode from './modes/SmsMode'; function AppLayout() { return ( @@ -44,11 +49,12 @@ function BottomInput() { switch (state.mode) { case 'text': return ; case 'url': return ; - default: return ( -
- 更多模式即将推出... -
- ); + case 'wifi': return ; + case 'vcard': return ; + case 'email': return ; + case 'phone': return ; + case 'sms': return ; + default: return ; } } diff --git a/gui/src-frontend/src/modes/EmailMode.tsx b/gui/src-frontend/src/modes/EmailMode.tsx new file mode 100644 index 0000000..ef66f88 --- /dev/null +++ b/gui/src-frontend/src/modes/EmailMode.tsx @@ -0,0 +1,28 @@ +import { useQrState } from '../store/qrContext'; +import { useQrEncode } from '../hooks/useQrEncode'; + +export default function EmailMode() { + const { state, dispatch } = useQrState(); + const { encode } = useQrEncode(); + + const update = (field: string, value: string) => { + const data = { ...state.formData, [field]: value }; + dispatch({ type: 'SET_FORM_DATA', payload: data }); + const mailto = `mailto:${data.to || ''}?subject=${encodeURIComponent(data.subject || '')}&body=${encodeURIComponent(data.body || '')}`; + encode(mailto); + }; + + return ( +
+ update('to', e.target.value)} + className="flex-1 px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-sm bg-transparent outline-none focus:ring-2 focus:ring-blue-500/30" /> + update('subject', e.target.value)} + className="flex-1 px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-sm bg-transparent outline-none focus:ring-2 focus:ring-blue-500/30" /> + update('body', e.target.value)} + className="flex-[2] px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-sm bg-transparent outline-none focus:ring-2 focus:ring-blue-500/30" /> +
+ ); +} diff --git a/gui/src-frontend/src/modes/PhoneMode.tsx b/gui/src-frontend/src/modes/PhoneMode.tsx new file mode 100644 index 0000000..91dca0a --- /dev/null +++ b/gui/src-frontend/src/modes/PhoneMode.tsx @@ -0,0 +1,18 @@ +import { useQrState } from '../store/qrContext'; +import { useQrEncode } from '../hooks/useQrEncode'; + +export default function PhoneMode() { + const { state, dispatch } = useQrState(); + const { encode } = useQrEncode(); + + const update = (number: string) => { + dispatch({ type: 'SET_FORM_DATA', payload: { number } }); + encode(`tel:${number}`); + }; + + return ( + update(e.target.value)} + className="w-full h-full px-4 text-sm bg-transparent outline-none placeholder-gray-400 dark:placeholder-gray-600 focus:ring-2 focus:ring-blue-500/30" /> + ); +} diff --git a/gui/src-frontend/src/modes/SmsMode.tsx b/gui/src-frontend/src/modes/SmsMode.tsx new file mode 100644 index 0000000..9775c0f --- /dev/null +++ b/gui/src-frontend/src/modes/SmsMode.tsx @@ -0,0 +1,24 @@ +import { useQrState } from '../store/qrContext'; +import { useQrEncode } from '../hooks/useQrEncode'; + +export default function SmsMode() { + const { state, dispatch } = useQrState(); + const { encode } = useQrEncode(); + + const update = (field: string, value: string) => { + const data = { ...state.formData, [field]: value }; + dispatch({ type: 'SET_FORM_DATA', payload: data }); + encode(`smsto:${data.number || ''}:${data.message || ''}`); + }; + + return ( +
+ update('number', e.target.value)} + className="flex-1 px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-sm bg-transparent outline-none focus:ring-2 focus:ring-blue-500/30" /> + update('message', e.target.value)} + className="flex-[2] px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-sm bg-transparent outline-none focus:ring-2 focus:ring-blue-500/30" /> +
+ ); +} diff --git a/gui/src-frontend/src/modes/VCardMode.tsx b/gui/src-frontend/src/modes/VCardMode.tsx new file mode 100644 index 0000000..e09ab14 --- /dev/null +++ b/gui/src-frontend/src/modes/VCardMode.tsx @@ -0,0 +1,33 @@ +import { useQrState } from '../store/qrContext'; +import { useQrEncode } from '../hooks/useQrEncode'; + +const FIELDS = [ + { key: 'name', placeholder: '姓名' }, + { key: 'phone', placeholder: '电话' }, + { key: 'email', placeholder: '邮箱' }, + { key: 'company', placeholder: '公司' }, + { key: 'address', placeholder: '地址' }, +]; + +export default function VCardMode() { + const { state, dispatch } = useQrState(); + const { encode } = useQrEncode(); + + const update = (field: string, value: string) => { + const data = { ...state.formData, [field]: value }; + dispatch({ type: 'SET_FORM_DATA', payload: data }); + const vcard = `BEGIN:VCARD\nVERSION:3.0\nFN:${data.name || ''}\nTEL:${data.phone || ''}\nEMAIL:${data.email || ''}\nORG:${data.company || ''}\nADR:${data.address || ''}\nEND:VCARD`; + encode(vcard); + }; + + return ( +
+ {FIELDS.map(f => ( + update(f.key, e.target.value)} + className="flex-1 px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-sm bg-transparent outline-none focus:ring-2 focus:ring-blue-500/30" /> + ))} +
+ ); +} diff --git a/gui/src-frontend/src/modes/WifiMode.tsx b/gui/src-frontend/src/modes/WifiMode.tsx new file mode 100644 index 0000000..82efda5 --- /dev/null +++ b/gui/src-frontend/src/modes/WifiMode.tsx @@ -0,0 +1,44 @@ +import { useQrState } from '../store/qrContext'; +import { useQrEncode } from '../hooks/useQrEncode'; + +export default function WifiMode() { + const { state, dispatch } = useQrState(); + const { encode } = useQrEncode(); + + const buildWifiText = (ssid: string, password: string, encryption: string, hidden: boolean) => { + if (!ssid) return ''; + return `WIFI:T:${encryption};S:${ssid};P:${password};${hidden ? 'H:true;' : ''};`; + }; + + const update = (field: string, value: string | boolean) => { + const data = { ...state.formData, [field]: String(value) }; + dispatch({ type: 'SET_FORM_DATA', payload: data }); + const wifiText = buildWifiText( + data.ssid || '', data.password || '', data.encryption || 'WPA', data.hidden === 'true' + ); + encode(wifiText); + }; + + return ( +
+ update('ssid', e.target.value)} + className="flex-1 px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-sm bg-transparent outline-none focus:ring-2 focus:ring-blue-500/30" /> + update('password', e.target.value)} + className="flex-1 px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-sm bg-transparent outline-none focus:ring-2 focus:ring-blue-500/30" /> + + +
+ ); +}