Skip to content

Commit 9dd3e19

Browse files
committed
fix: improve contact form mailto functionality with better error handling
- Replace window.location.href with hidden anchor tag approach for better browser compatibility - Add visibility/blur event detection to check if email app opened successfully - Provide fallback modal with copy-to-clipboard functionality when mailto fails - Simplify environment detection logic - Add alternative contact methods (GitHub Issues, Facebook, Discord) - Improve user experience with better error messages and guidance Fixes #39
1 parent 3399ad4 commit 9dd3e19

1 file changed

Lines changed: 189 additions & 12 deletions

File tree

src/components/contact/ContactForm.vue

Lines changed: 189 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
{{ isSubmitting ? '送出中...' : '送出' }}
7070
</button>
7171
</form>
72-
72+
7373
<!-- Ko-fi 贊助按鈕 -->
7474
<div class="has-flex-center has-top-spaced-large">
7575
<div class="ts-text is-secondary has-bottom-spaced">如果您覺得我的內容對您有幫助,歡迎贊助支持我繼續創作!</div>
@@ -92,8 +92,132 @@ const form = reactive({
9292
9393
const isSubmitting = ref(false)
9494
95+
// 檢測是否支援 mailto(簡化版本)
96+
const isMailtoSupported = () => {
97+
// 假設大部分桌面環境都支援 mailto
98+
// 只有在明確知道不支援的情況下才返回 false
99+
return true
100+
}
101+
102+
// 複製內容到剪貼簿
103+
const copyToClipboard = async (text) => {
104+
try {
105+
if (navigator.clipboard && window.isSecureContext) {
106+
await navigator.clipboard.writeText(text)
107+
return true
108+
} else {
109+
// 備用方案:使用 textarea
110+
const textArea = document.createElement('textarea')
111+
textArea.value = text
112+
textArea.style.position = 'fixed'
113+
textArea.style.left = '-9999px'
114+
textArea.style.top = '-9999px'
115+
document.body.appendChild(textArea)
116+
textArea.focus()
117+
textArea.select()
118+
119+
try {
120+
const successful = document.execCommand('copy')
121+
document.body.removeChild(textArea)
122+
return successful
123+
} catch (err) {
124+
document.body.removeChild(textArea)
125+
return false
126+
}
127+
}
128+
} catch (err) {
129+
console.error('無法複製到剪貼簿:', err)
130+
return false
131+
}
132+
}
133+
134+
// 顯示郵件內容模態框
135+
const showEmailModal = (subject, body) => {
136+
const modalContent = `
137+
<div class="ts-modal is-visible" id="email-modal">
138+
<div class="content">
139+
<div class="ts-content has-vertically-padded">
140+
<div class="ts-header is-large has-bottom-spaced">
141+
<span class="ts-icon is-envelope-icon"></span>
142+
郵件內容
143+
</div>
144+
<div class="ts-text is-secondary has-bottom-spaced">
145+
由於環境限制,無法直接開啟郵件應用程式。請複製以下內容手動發送郵件:
146+
</div>
147+
148+
<div class="ts-segment is-secondary has-bottom-spaced">
149+
<div class="ts-text is-label">收件人</div>
150+
<div class="ts-text is-code">kageryo@coderyo.com</div>
151+
</div>
152+
153+
<div class="ts-segment is-secondary has-bottom-spaced">
154+
<div class="ts-text is-label">主旨</div>
155+
<div class="ts-text is-code">${subject}</div>
156+
</div>
157+
158+
<div class="ts-segment is-secondary has-bottom-spaced">
159+
<div class="ts-text is-label">內容</div>
160+
<div class="ts-text is-code" style="white-space: pre-wrap; font-family: monospace;">${body}</div>
161+
</div>
162+
</div>
163+
164+
<div class="ts-divider"></div>
165+
166+
<div class="ts-content is-tertiary">
167+
<div class="ts-grid is-2-columns">
168+
<div class="column">
169+
<button class="ts-button is-fluid" onclick="copyEmailContent()">
170+
<span class="ts-icon is-copy-icon"></span>
171+
複製全部內容
172+
</button>
173+
</div>
174+
<div class="column">
175+
<button class="ts-button is-outlined is-fluid" onclick="closeEmailModal()">
176+
關閉
177+
</button>
178+
</div>
179+
</div>
180+
</div>
181+
</div>
182+
</div>`
183+
184+
// 移除現有的模態框
185+
const existingModal = document.getElementById('email-modal')
186+
if (existingModal) {
187+
existingModal.remove()
188+
}
189+
190+
// 添加模態框到 body
191+
document.body.insertAdjacentHTML('beforeend', modalContent)
192+
193+
// 添加全域函數
194+
window.copyEmailContent = async () => {
195+
const emailContent = `收件人: kageryo@coderyo.com
196+
主旨: ${subject}
197+
198+
內容:
199+
${body}`
200+
201+
const success = await copyToClipboard(emailContent)
202+
if (success) {
203+
alert('郵件內容已複製到剪貼簿!您可以貼到您的郵件應用程式中。')
204+
} else {
205+
alert('複製失敗,請手動選取複製上述內容。')
206+
}
207+
}
208+
209+
window.closeEmailModal = () => {
210+
const modal = document.getElementById('email-modal')
211+
if (modal) {
212+
modal.remove()
213+
}
214+
delete window.copyEmailContent
215+
delete window.closeEmailModal
216+
}
217+
}
218+
95219
// 發送郵件功能
96-
const sendEmail = () => {
220+
const sendEmail = async () => {
97221
isSubmitting.value = true
98222
99223
try {
@@ -106,20 +230,73 @@ const sendEmail = () => {
106230
${form.message}`
107231
108232
const mailtoURL = `mailto:kageryo@coderyo.com?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`
109-
window.location.href = mailtoURL
110233
111-
// 重置表單
112-
Object.assign(form, {
113-
name: '',
114-
gender: 'male',
115-
email: '',
116-
message: ''
117-
})
234+
console.log('嘗試打開郵件應用程式:', mailtoURL)
235+
236+
// 創建一個隱藏的 a 標籤來觸發 mailto
237+
const link = document.createElement('a')
238+
link.href = mailtoURL
239+
link.style.display = 'none'
240+
document.body.appendChild(link)
241+
link.click()
242+
document.body.removeChild(link)
243+
244+
// 檢查是否成功觸發
245+
let mailAppOpened = false
246+
247+
// 監聽視窗焦點變化來檢測郵件應用程式是否打開
248+
const handleVisibilityChange = () => {
249+
if (document.hidden) {
250+
mailAppOpened = true
251+
document.removeEventListener('visibilitychange', handleVisibilityChange)
252+
}
253+
}
254+
255+
const handleBlur = () => {
256+
mailAppOpened = true
257+
window.removeEventListener('blur', handleBlur)
258+
}
259+
260+
document.addEventListener('visibilitychange', handleVisibilityChange)
261+
window.addEventListener('blur', handleBlur)
262+
263+
// 等待一小段時間檢查結果
264+
setTimeout(() => {
265+
document.removeEventListener('visibilitychange', handleVisibilityChange)
266+
window.removeEventListener('blur', handleBlur)
267+
268+
if (mailAppOpened) {
269+
// 郵件應用程式似乎有打開
270+
alert('郵件應用程式已開啟,請確認發送!')
271+
272+
// 重置表單
273+
Object.assign(form, {
274+
name: '',
275+
gender: 'male',
276+
email: '',
277+
message: ''
278+
})
279+
} else {
280+
// 郵件應用程式沒有打開,提供備用方案
281+
const userConfirm = confirm('郵件應用程式可能沒有正確開啟。\n\n點擊「確定」查看備用方案,或點擊「取消」保留表單內容。')
282+
283+
if (userConfirm) {
284+
showEmailModal(subject, body)
285+
286+
// 重置表單
287+
Object.assign(form, {
288+
name: '',
289+
gender: 'male',
290+
email: '',
291+
message: ''
292+
})
293+
}
294+
}
295+
}, 2000)
118296
119-
alert('郵件應用程式已開啟,請確認發送!')
120297
} catch (error) {
121298
console.error('發送郵件時發生錯誤:', error)
122-
alert('發送失敗,請稍後再試或直接發送郵件到 kageryo@coderyo.com')
299+
alert(`發送失敗${error.message}\n\n請直接發送郵件到 kageryo@coderyo.com 或使用其他聯絡方式。`)
123300
} finally {
124301
isSubmitting.value = false
125302
}

0 commit comments

Comments
 (0)