# 前端使用示例 本文档提供 ImageUpload 模块在前端(Shop)的使用示例。 ## 目录 - [Vue 组件使用](#vue-组件使用) - [JavaScript Fetch API](#javascript-fetch-api) - [jQuery AJAX](#jquery-ajax) - [React 组件](#react-组件) - [实际应用场景](#实际应用场景) ## Vue 组件使用 ### 1. 在 Blade 模板中使用 ```blade {{-- 在 Blade 文件中引入 Vue 组件 --}} @extends('shop::layouts.master') @section('content')

上传头像

{{-- 单张图片上传 --}}

头像预览:

Avatar

上传产品图片

{{-- 多张图片上传 --}}

已上传 {{ productImages.length }} 张图片:

@endsection @push('scripts') @endpush ``` ### 2. 在 Vue SFC 中使用 ```vue ``` ## JavaScript Fetch API ### 基础用法 ```javascript // 获取 CSRF Token function getCsrfToken() { return document.querySelector('meta[name="csrf-token"]')?.content; } // 上传单张图片 async function uploadSingleImage(file, directory = 'shop/uploads') { const formData = new FormData(); formData.append('image', file); formData.append('directory', directory); try { const response = await fetch('/api/image-upload/upload', { method: 'POST', body: formData, headers: { 'X-CSRF-TOKEN': getCsrfToken() } }); const data = await response.json(); if (data.success) { return data.data; // { url, path, ... } } else { throw new Error(data.message); } } catch (error) { console.error('Upload failed:', error); throw error; } } // 上传多张图片 async function uploadMultipleImages(files, directory = 'shop/uploads') { const formData = new FormData(); files.forEach(file => { formData.append('images[]', file); }); formData.append('directory', directory); try { const response = await fetch('/api/image-upload/upload-multiple', { method: 'POST', body: formData, headers: { 'X-CSRF-TOKEN': getCsrfToken() } }); const data = await response.json(); if (data.success) { return data.data; // Array of results } else { throw new Error(data.message); } } catch (error) { console.error('Upload failed:', error); throw error; } } // 使用示例 document.getElementById('imageInput').addEventListener('change', async (e) => { const file = e.target.files[0]; if (!file) return; try { const result = await uploadSingleImage(file, 'user/photos'); console.log('上传成功:', result.url); // 显示预览 const preview = document.getElementById('preview'); preview.src = result.url; preview.style.display = 'block'; } catch (error) { alert('上传失败: ' + error.message); } }); ``` ### 带进度条的上传 ```javascript function uploadWithProgress(file, onProgress) { return new Promise((resolve, reject) => { const formData = new FormData(); formData.append('image', file); formData.append('directory', 'shop/uploads'); const xhr = new XMLHttpRequest(); xhr.upload.addEventListener('progress', (event) => { if (event.lengthComputable) { const percentComplete = (event.loaded / event.total) * 100; onProgress(percentComplete); } }); xhr.addEventListener('load', () => { if (xhr.status === 200) { const data = JSON.parse(xhr.responseText); if (data.success) { resolve(data.data); } else { reject(new Error(data.message)); } } else { reject(new Error('Upload failed')); } }); xhr.addEventListener('error', () => { reject(new Error('Network error')); }); xhr.open('POST', '/api/image-upload/upload'); xhr.setRequestHeader('X-CSRF-TOKEN', getCsrfToken()); xhr.send(formData); }); } // 使用示例 const fileInput = document.getElementById('imageInput'); const progressBar = document.getElementById('progressBar'); fileInput.addEventListener('change', async (e) => { const file = e.target.files[0]; if (!file) return; try { const result = await uploadWithProgress(file, (progress) => { progressBar.value = progress; progressBar.textContent = Math.round(progress) + '%'; }); console.log('上传完成:', result.url); } catch (error) { alert('上传失败: ' + error.message); } }); ``` ## jQuery AJAX ```javascript // 单张图片上传 function uploadImage(file) { var formData = new FormData(); formData.append('image', file); formData.append('directory', 'shop/uploads'); $.ajax({ url: '/api/image-upload/upload', type: 'POST', data: formData, processData: false, contentType: false, headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') }, success: function(response) { if (response.success) { console.log('上传成功:', response.data.url); $('#preview').attr('src', response.data.url).show(); } else { alert('上传失败: ' + response.message); } }, error: function(xhr) { alert('请求失败'); } }); } // 绑定事件 $('#imageInput').on('change', function() { var file = this.files[0]; if (file) { uploadImage(file); } }); ``` ## React 组件 ```jsx import React, { useState } from 'react'; function ImageUpload({ multiple = false, directory = 'shop/uploads', onUpload }) { const [uploading, setUploading] = useState(false); const [preview, setPreview] = useState(null); const handleUpload = async (files) => { setUploading(true); const formData = new FormData(); if (multiple) { Array.from(files).forEach(file => { formData.append('images[]', file); }); } else { formData.append('image', files[0]); } formData.append('directory', directory); try { const response = await fetch( multiple ? '/api/image-upload/upload-multiple' : '/api/image-upload/upload', { method: 'POST', body: formData, headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content } } ); const data = await response.json(); if (data.success) { if (!multiple) { setPreview(data.data.url); } onUpload(data.data); } else { alert('上传失败: ' + data.message); } } catch (error) { alert('上传失败: ' + error.message); } finally { setUploading(false); } }; return (
handleUpload(e.target.files)} disabled={uploading} /> {uploading &&

上传中...

} {preview && !multiple && ( Preview )}
); } export default ImageUpload; ``` ## 实际应用场景 ### 1. 用户头像上传 ```javascript // 在用户资料页面 async function updateAvatar(file) { const result = await uploadSingleImage(file, 'user/avatars'); // 更新用户资料 await fetch('/api/customer/profile', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': getCsrfToken() }, body: JSON.stringify({ avatar: result.path }) }); // 更新页面显示 document.getElementById('userAvatar').src = result.url; } ``` ### 2. 商品评价图片上传 ```javascript // 在评价表单中 async function submitReview(reviewData, images) { let imagePaths = []; if (images && images.length > 0) { const uploadResults = await uploadMultipleImages(images, 'review/images'); imagePaths = uploadResults .filter(r => r.success) .map(r => r.path); } // 提交评价 const response = await fetch('/api/product/review', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': getCsrfToken() }, body: JSON.stringify({ ...reviewData, images: imagePaths }) }); return response.json(); } ``` ### 3. 客服聊天图片发送 ```javascript // 在聊天界面 async function sendChatMessage(message, image) { let imageUrl = null; if (image) { const result = await uploadSingleImage(image, 'chat/messages'); imageUrl = result.url; } // 发送消息 await fetch('/api/chat/send', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': getCsrfToken() }, body: JSON.stringify({ message: message, image_url: imageUrl }) }); } ``` ## 注意事项 1. **CSRF Token**: 确保在所有请求中包含 CSRF token 2. **文件大小限制**: 前端验证文件大小,避免上传过大文件 3. **错误处理**: 妥善处理上传失败的情况 4. **用户体验**: 显示上传进度和状态反馈 5. **图片预览**: 上传前显示预览,提升用户体验 6. **安全性**: 验证文件类型,防止恶意文件上传 ## 常见问题 **Q: 如何限制上传的文件类型?** A: 在 input 元素上使用 `accept` 属性:`accept="image/jpeg,image/png,image/gif"` **Q: 如何实现拖拽上传?** A: 监听 `dragover`, `dragleave`, `drop` 事件,参考 Vue 组件中的实现 **Q: 上传大文件时超时怎么办?** A: 增加服务器超时时间,或实现分片上传 **Q: 如何在移动端优化?** A: 使用 `capture` 属性直接调用相机:``