560 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			HTML
		
	
	
	
		
		
			
		
	
	
			560 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			HTML
		
	
	
	
|  | <!DOCTYPE html> | |||
|  | <html lang="zh-CN"> | |||
|  | <head> | |||
|  |     <meta charset="UTF-8"> | |||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
|  |     <title>退款申请</title> | |||
|  |     <style> | |||
|  |         * { | |||
|  |             margin: 0; | |||
|  |             padding: 0; | |||
|  |             box-sizing: border-box; | |||
|  |         } | |||
|  | 
 | |||
|  |         body { | |||
|  |             font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif; | |||
|  |             background-color: #f5f5f5; | |||
|  |             padding-bottom: 80px; | |||
|  |         } | |||
|  | 
 | |||
|  |         .header { | |||
|  |             background-color: #013820; | |||
|  |             color: white; | |||
|  |             padding: 15px 20px; | |||
|  |             font-size: 18px; | |||
|  |             font-weight: bold; | |||
|  |             text-align: center; | |||
|  |         } | |||
|  | 
 | |||
|  |         .feed-all { | |||
|  |             min-height: 100vh; | |||
|  |         } | |||
|  | 
 | |||
|  |         .group { | |||
|  |             background-color: white; | |||
|  |             margin: 10px; | |||
|  |             border-radius: 8px; | |||
|  |             overflow: hidden; | |||
|  |             border: 1px solid #e5e5e5; | |||
|  |         } | |||
|  | 
 | |||
|  |         .form-item { | |||
|  |             padding: 15px; | |||
|  |             border-bottom: 1px solid #f0f0f0; | |||
|  |         } | |||
|  | 
 | |||
|  |         .form-item:last-child { | |||
|  |             border-bottom: none; | |||
|  |         } | |||
|  | 
 | |||
|  |         .form-label { | |||
|  |             display: block; | |||
|  |             color: #333; | |||
|  |             font-size: 15px; | |||
|  |             margin-bottom: 10px; | |||
|  |             font-weight: 500; | |||
|  |         } | |||
|  | 
 | |||
|  |         .form-label.required::before { | |||
|  |             content: '*'; | |||
|  |             color: #ff0000; | |||
|  |             margin-right: 4px; | |||
|  |         } | |||
|  | 
 | |||
|  |         .textarea-wrapper { | |||
|  |             position: relative; | |||
|  |         } | |||
|  | 
 | |||
|  |         .textarea-field { | |||
|  |             width: 100%; | |||
|  |             min-height: 120px; | |||
|  |             padding: 10px; | |||
|  |             border: 1px solid #dcdcdc; | |||
|  |             border-radius: 4px; | |||
|  |             font-size: 14px; | |||
|  |             resize: vertical; | |||
|  |             font-family: inherit; | |||
|  |             line-height: 1.5; | |||
|  |         } | |||
|  | 
 | |||
|  |         .textarea-field:focus { | |||
|  |             outline: none; | |||
|  |             border-color: #013820; | |||
|  |         } | |||
|  | 
 | |||
|  |         .textarea-field::placeholder { | |||
|  |             color: #999; | |||
|  |         } | |||
|  | 
 | |||
|  |         .word-limit { | |||
|  |             text-align: right; | |||
|  |             color: #999; | |||
|  |             font-size: 12px; | |||
|  |             margin-top: 5px; | |||
|  |         } | |||
|  | 
 | |||
|  |         .upload-section { | |||
|  |             margin-top: 10px; | |||
|  |         } | |||
|  | 
 | |||
|  |         .upload-wrapper { | |||
|  |             display: flex; | |||
|  |             flex-wrap: wrap; | |||
|  |             gap: 10px; | |||
|  |             margin-top: 10px; | |||
|  |         } | |||
|  | 
 | |||
|  |         .upload-item { | |||
|  |             position: relative; | |||
|  |             width: 80px; | |||
|  |             height: 80px; | |||
|  |             border: 1px dashed #dcdcdc; | |||
|  |             border-radius: 4px; | |||
|  |             overflow: hidden; | |||
|  |         } | |||
|  | 
 | |||
|  |         .upload-item img { | |||
|  |             width: 100%; | |||
|  |             height: 100%; | |||
|  |             object-fit: cover; | |||
|  |             background: linear-gradient(135deg, #e0e0e0 0%, #f5f5f5 100%); | |||
|  |         } | |||
|  | 
 | |||
|  |         .upload-preview { | |||
|  |             width: 100%; | |||
|  |             height: 100%; | |||
|  |             background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%); | |||
|  |             display: flex; | |||
|  |             align-items: center; | |||
|  |             justify-content: center; | |||
|  |             font-size: 36px; | |||
|  |         } | |||
|  | 
 | |||
|  |         .upload-item .remove-btn { | |||
|  |             position: absolute; | |||
|  |             top: -8px; | |||
|  |             right: -8px; | |||
|  |             width: 20px; | |||
|  |             height: 20px; | |||
|  |             background-color: #ff0000; | |||
|  |             color: white; | |||
|  |             border: none; | |||
|  |             border-radius: 50%; | |||
|  |             cursor: pointer; | |||
|  |             display: flex; | |||
|  |             align-items: center; | |||
|  |             justify-content: center; | |||
|  |             font-size: 14px; | |||
|  |             line-height: 1; | |||
|  |         } | |||
|  | 
 | |||
|  |         .upload-btn { | |||
|  |             width: 80px; | |||
|  |             height: 80px; | |||
|  |             border: 1px dashed #dcdcdc; | |||
|  |             border-radius: 4px; | |||
|  |             background-color: #fafafa; | |||
|  |             cursor: pointer; | |||
|  |             display: flex; | |||
|  |             flex-direction: column; | |||
|  |             align-items: center; | |||
|  |             justify-content: center; | |||
|  |             color: #999; | |||
|  |             font-size: 12px; | |||
|  |             transition: all 0.3s; | |||
|  |         } | |||
|  | 
 | |||
|  |         .upload-btn:hover { | |||
|  |             border-color: #013820; | |||
|  |             color: #013820; | |||
|  |         } | |||
|  | 
 | |||
|  |         .upload-btn .icon { | |||
|  |             font-size: 24px; | |||
|  |             margin-bottom: 5px; | |||
|  |         } | |||
|  | 
 | |||
|  |         .upload-input { | |||
|  |             display: none; | |||
|  |         } | |||
|  | 
 | |||
|  |         .footer { | |||
|  |             position: fixed; | |||
|  |             bottom: 0; | |||
|  |             left: 0; | |||
|  |             right: 0; | |||
|  |             padding: 15px 25px; | |||
|  |             background-color: white; | |||
|  |             box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1); | |||
|  |         } | |||
|  | 
 | |||
|  |         .submit-btn { | |||
|  |             width: 100%; | |||
|  |             padding: 15px; | |||
|  |             background-color: #013820; | |||
|  |             color: white; | |||
|  |             border: none; | |||
|  |             border-radius: 4px; | |||
|  |             font-size: 16px; | |||
|  |             font-weight: bold; | |||
|  |             cursor: pointer; | |||
|  |             transition: all 0.3s; | |||
|  |         } | |||
|  | 
 | |||
|  |         .submit-btn:hover { | |||
|  |             background-color: #025530; | |||
|  |         } | |||
|  | 
 | |||
|  |         .submit-btn:active { | |||
|  |             transform: scale(0.98); | |||
|  |         } | |||
|  | 
 | |||
|  |         .error-message { | |||
|  |             color: #ff0000; | |||
|  |             font-size: 12px; | |||
|  |             margin-top: 5px; | |||
|  |         } | |||
|  | 
 | |||
|  |         .toast { | |||
|  |             position: fixed; | |||
|  |             top: 50%; | |||
|  |             left: 50%; | |||
|  |             transform: translate(-50%, -50%); | |||
|  |             background-color: rgba(0, 0, 0, 0.7); | |||
|  |             color: white; | |||
|  |             padding: 15px 25px; | |||
|  |             border-radius: 4px; | |||
|  |             z-index: 1000; | |||
|  |             display: none; | |||
|  |         } | |||
|  | 
 | |||
|  |         .toast.show { | |||
|  |             display: block; | |||
|  |             animation: fadeIn 0.3s; | |||
|  |         } | |||
|  | 
 | |||
|  |         @keyframes fadeIn { | |||
|  |             from { opacity: 0; } | |||
|  |             to { opacity: 1; } | |||
|  |         } | |||
|  | 
 | |||
|  |         .loading { | |||
|  |             position: fixed; | |||
|  |             top: 0; | |||
|  |             left: 0; | |||
|  |             right: 0; | |||
|  |             bottom: 0; | |||
|  |             background-color: rgba(0, 0, 0, 0.5); | |||
|  |             display: none; | |||
|  |             align-items: center; | |||
|  |             justify-content: center; | |||
|  |             z-index: 1000; | |||
|  |         } | |||
|  | 
 | |||
|  |         .loading.show { | |||
|  |             display: flex; | |||
|  |         } | |||
|  | 
 | |||
|  |         .loading-content { | |||
|  |             background-color: rgba(0, 0, 0, 0.7); | |||
|  |             color: white; | |||
|  |             padding: 20px 30px; | |||
|  |             border-radius: 8px; | |||
|  |             text-align: center; | |||
|  |         } | |||
|  | 
 | |||
|  |         .spinner { | |||
|  |             border: 3px solid rgba(255, 255, 255, 0.3); | |||
|  |             border-top: 3px solid white; | |||
|  |             border-radius: 50%; | |||
|  |             width: 40px; | |||
|  |             height: 40px; | |||
|  |             animation: spin 1s linear infinite; | |||
|  |             margin: 0 auto 10px; | |||
|  |         } | |||
|  | 
 | |||
|  |         @keyframes spin { | |||
|  |             0% { transform: rotate(0deg); } | |||
|  |             100% { transform: rotate(360deg); } | |||
|  |         } | |||
|  |     </style> | |||
|  | </head> | |||
|  | <body> | |||
|  |     <div class="header">退款申请</div> | |||
|  | 
 | |||
|  |     <div class="feed-all"> | |||
|  |         <form id="refundForm"> | |||
|  |             <div class="group"> | |||
|  |                 <div class="form-item"> | |||
|  |                     <label class="form-label required">申请理由</label> | |||
|  |                     <div class="textarea-wrapper"> | |||
|  |                         <textarea | |||
|  |                             id="reason" | |||
|  |                             class="textarea-field" | |||
|  |                             placeholder="请输入申请理由" | |||
|  |                             maxlength="300" | |||
|  |                         ></textarea> | |||
|  |                         <div class="word-limit"> | |||
|  |                             <span id="wordCount">0</span>/300 | |||
|  |                         </div> | |||
|  |                     </div> | |||
|  |                     <div id="reasonError" class="error-message" style="display: none;">请输入申请理由</div> | |||
|  |                 </div> | |||
|  | 
 | |||
|  |                 <div class="form-item"> | |||
|  |                     <label class="form-label required">上传凭证</label> | |||
|  |                     <div class="upload-section"> | |||
|  |                         <div class="upload-wrapper" id="uploadWrapper"> | |||
|  |                             <div class="upload-btn" id="uploadBtn"> | |||
|  |                                 <div class="icon">+</div> | |||
|  |                                 <div>上传图片</div> | |||
|  |                             </div> | |||
|  |                         </div> | |||
|  |                         <input | |||
|  |                             type="file" | |||
|  |                             id="fileInput" | |||
|  |                             class="upload-input" | |||
|  |                             accept="image/*" | |||
|  |                             multiple | |||
|  |                         > | |||
|  |                     </div> | |||
|  |                     <div id="uploadError" class="error-message" style="display: none;">请上传凭证</div> | |||
|  |                 </div> | |||
|  |             </div> | |||
|  |         </form> | |||
|  |     </div> | |||
|  | 
 | |||
|  |     <div class="footer"> | |||
|  |         <button class="submit-btn" id="submitBtn">提交</button> | |||
|  |     </div> | |||
|  | 
 | |||
|  |     <div id="toast" class="toast"></div> | |||
|  |     <div id="loading" class="loading"> | |||
|  |         <div class="loading-content"> | |||
|  |             <div class="spinner"></div> | |||
|  |             <div>申请中...</div> | |||
|  |         </div> | |||
|  |     </div> | |||
|  | 
 | |||
|  |     <script> | |||
|  |         // 获取URL参数 | |||
|  |         function getQueryParam(name) { | |||
|  |             const urlParams = new URLSearchParams(window.location.search); | |||
|  |             return urlParams.get(name); | |||
|  |         } | |||
|  | 
 | |||
|  |         // 订单编号(从URL参数获取) | |||
|  |         const unitOrderNo = getQueryParam('unitOrderNo') || ''; | |||
|  | 
 | |||
|  |         // 文件列表 | |||
|  |         let fileList = []; | |||
|  |         const MAX_FILES = 5; | |||
|  | 
 | |||
|  |         // DOM元素 | |||
|  |         const reasonTextarea = document.getElementById('reason'); | |||
|  |         const wordCountSpan = document.getElementById('wordCount'); | |||
|  |         const uploadBtn = document.getElementById('uploadBtn'); | |||
|  |         const fileInput = document.getElementById('fileInput'); | |||
|  |         const uploadWrapper = document.getElementById('uploadWrapper'); | |||
|  |         const submitBtn = document.getElementById('submitBtn'); | |||
|  |         const reasonError = document.getElementById('reasonError'); | |||
|  |         const uploadError = document.getElementById('uploadError'); | |||
|  |         const toast = document.getElementById('toast'); | |||
|  |         const loading = document.getElementById('loading'); | |||
|  | 
 | |||
|  |         // 字数统计 | |||
|  |         reasonTextarea.addEventListener('input', function() { | |||
|  |             const length = this.value.length; | |||
|  |             wordCountSpan.textContent = length; | |||
|  |             if (length > 0) { | |||
|  |                 reasonError.style.display = 'none'; | |||
|  |             } | |||
|  |         }); | |||
|  | 
 | |||
|  |         // 点击上传按钮 | |||
|  |         uploadBtn.addEventListener('click', function() { | |||
|  |             if (fileList.length < MAX_FILES) { | |||
|  |                 fileInput.click(); | |||
|  |             } else { | |||
|  |                 showToast('最多只能上传5张图片'); | |||
|  |             } | |||
|  |         }); | |||
|  | 
 | |||
|  |         // 文件选择 | |||
|  |         fileInput.addEventListener('change', function(e) { | |||
|  |             const files = Array.from(e.target.files); | |||
|  | 
 | |||
|  |             if (fileList.length + files.length > MAX_FILES) { | |||
|  |                 showToast(`最多只能上传${MAX_FILES}张图片`); | |||
|  |                 return; | |||
|  |             } | |||
|  | 
 | |||
|  |             files.forEach(file => { | |||
|  |                 if (file.type.startsWith('image/')) { | |||
|  |                     const reader = new FileReader(); | |||
|  |                     reader.onload = function(event) { | |||
|  |                         fileList.push({ | |||
|  |                             file: file, | |||
|  |                             url: event.target.result | |||
|  |                         }); | |||
|  |                         renderFiles(); | |||
|  |                         uploadError.style.display = 'none'; | |||
|  |                     }; | |||
|  |                     reader.readAsDataURL(file); | |||
|  |                 } | |||
|  |             }); | |||
|  | 
 | |||
|  |             // 清空input,以便可以重复选择同一文件 | |||
|  |             fileInput.value = ''; | |||
|  |         }); | |||
|  | 
 | |||
|  |         // 渲染文件列表 | |||
|  |         function renderFiles() { | |||
|  |             // 清空现有内容 | |||
|  |             uploadWrapper.innerHTML = ''; | |||
|  | 
 | |||
|  |             // 渲染已上传的图片 | |||
|  |             fileList.forEach((item, index) => { | |||
|  |                 const div = document.createElement('div'); | |||
|  |                 div.className = 'upload-item'; | |||
|  |                 // 使用表情符号作为占位符 | |||
|  |                 const emojis = ['📷', '🖼️', '🎨', '📸', '🌅']; | |||
|  |                 const emoji = emojis[index % emojis.length]; | |||
|  |                 div.innerHTML = ` | |||
|  |                     <div class="upload-preview">${emoji}</div> | |||
|  |                     <button class="remove-btn" onclick="removeFile(${index})">×</button> | |||
|  |                 `; | |||
|  |                 uploadWrapper.appendChild(div); | |||
|  |             }); | |||
|  | 
 | |||
|  |             // 如果未达到上限,显示上传按钮 | |||
|  |             if (fileList.length < MAX_FILES) { | |||
|  |                 const uploadBtnClone = document.createElement('div'); | |||
|  |                 uploadBtnClone.className = 'upload-btn'; | |||
|  |                 uploadBtnClone.id = 'uploadBtn'; | |||
|  |                 uploadBtnClone.innerHTML = ` | |||
|  |                     <div class="icon">+</div> | |||
|  |                     <div>上传图片</div> | |||
|  |                 `; | |||
|  |                 uploadBtnClone.addEventListener('click', function() { | |||
|  |                     fileInput.click(); | |||
|  |                 }); | |||
|  |                 uploadWrapper.appendChild(uploadBtnClone); | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         // 删除文件 | |||
|  |         window.removeFile = function(index) { | |||
|  |             fileList.splice(index, 1); | |||
|  |             renderFiles(); | |||
|  |         }; | |||
|  | 
 | |||
|  |         // 显示提示 | |||
|  |         function showToast(message, duration = 2000) { | |||
|  |             toast.textContent = message; | |||
|  |             toast.classList.add('show'); | |||
|  |             setTimeout(() => { | |||
|  |                 toast.classList.remove('show'); | |||
|  |             }, duration); | |||
|  |         } | |||
|  | 
 | |||
|  |         // 显示加载中 | |||
|  |         function showLoading() { | |||
|  |             loading.classList.add('show'); | |||
|  |         } | |||
|  | 
 | |||
|  |         // 隐藏加载中 | |||
|  |         function hideLoading() { | |||
|  |             loading.classList.remove('show'); | |||
|  |         } | |||
|  | 
 | |||
|  |         // 表单验证 | |||
|  |         function validateForm() { | |||
|  |             let isValid = true; | |||
|  | 
 | |||
|  |             // 验证申请理由 | |||
|  |             if (!reasonTextarea.value.trim()) { | |||
|  |                 reasonError.style.display = 'block'; | |||
|  |                 isValid = false; | |||
|  |             } else { | |||
|  |                 reasonError.style.display = 'none'; | |||
|  |             } | |||
|  | 
 | |||
|  |             // 验证上传凭证 | |||
|  |             if (fileList.length === 0) { | |||
|  |                 uploadError.style.display = 'block'; | |||
|  |                 showToast('请上传凭证'); | |||
|  |                 isValid = false; | |||
|  |             } else { | |||
|  |                 uploadError.style.display = 'none'; | |||
|  |             } | |||
|  | 
 | |||
|  |             return isValid; | |||
|  |         } | |||
|  | 
 | |||
|  |         // 模拟文件上传到服务器 | |||
|  |         async function uploadFilesToServer(files) { | |||
|  |             // 这里应该是实际的上传逻辑,返回文件URL列表 | |||
|  |             // 现在使用模拟数据 | |||
|  |             const urls = files.map((item, index) => { | |||
|  |                 return `https://example.com/uploads/refund_${Date.now()}_${index}.jpg`; | |||
|  |             }); | |||
|  |             return urls; | |||
|  |         } | |||
|  | 
 | |||
|  |         // 提交表单 | |||
|  |         submitBtn.addEventListener('click', async function() { | |||
|  |             if (!validateForm()) { | |||
|  |                 return; | |||
|  |             } | |||
|  | 
 | |||
|  |             showLoading(); | |||
|  | 
 | |||
|  |             try { | |||
|  |                 // 模拟上传文件并获取URL | |||
|  |                 const imageUrls = await uploadFilesToServer(fileList); | |||
|  | 
 | |||
|  |                 // 准备提交数据 | |||
|  |                 const submitData = { | |||
|  |                     unitOrderNo: unitOrderNo, | |||
|  |                     reason: reasonTextarea.value.trim(), | |||
|  |                     img: imageUrls.join(',') | |||
|  |                 }; | |||
|  | 
 | |||
|  |                 console.log('提交数据:', submitData); | |||
|  | 
 | |||
|  |                 // 模拟API请求 | |||
|  |                 // 实际项目中应该调用真实的API接口 | |||
|  |                 await new Promise((resolve, reject) => { | |||
|  |                     setTimeout(() => { | |||
|  |                         // 模拟成功 | |||
|  |                         resolve(); | |||
|  |                         // 模拟失败,取消下面的注释 | |||
|  |                         // reject(new Error('提交失败')); | |||
|  |                     }, 1500); | |||
|  |                 }); | |||
|  | 
 | |||
|  |                 hideLoading(); | |||
|  |                 showToast('申请成功,等待审核中'); | |||
|  | 
 | |||
|  |                 // 延迟后返回上一页 | |||
|  |                 setTimeout(() => { | |||
|  |                     window.history.back(); | |||
|  |                     console.log('返回上一页并刷新订单列表'); | |||
|  |                 }, 1500); | |||
|  | 
 | |||
|  |             } catch (error) { | |||
|  |                 hideLoading(); | |||
|  |                 showToast('申请失败,请联系商家'); | |||
|  |                 console.error('提交失败:', error); | |||
|  |             } | |||
|  |         }); | |||
|  | 
 | |||
|  |         // 页面加载完成 | |||
|  |         document.addEventListener('DOMContentLoaded', function() { | |||
|  |             console.log('页面加载完成,订单编号:', unitOrderNo); | |||
|  |         }); | |||
|  |     </script> | |||
|  | </body> | |||
|  | </html> |