# 前端使用示例
本文档提供 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')
上传头像
{{-- 单张图片上传 --}}
头像预览:
上传产品图片
{{-- 多张图片上传 --}}
已上传 {{ 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 && (

)}
);
}
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` 属性直接调用相机:``