寻求Vue3+Inertia+Laravel10集成Filepond实现完整CRUD的方案
Vue + Inertia.js + Laravel + FilePond 完整CRUD实现指南
核心依赖确认
确保已安装以下依赖:
vue-filepond+ 插件:filepond-plugin-image-preview、filepond-plugin-file-validate-type、filepond-plugin-reorder(用于排序)laravel-filepond(后端处理)
前端Vue组件实现(含所有功能点)
组件代码
<template> <div class="product-form"> <form @submit.prevent="handleSubmit"> <!-- 产品基础字段 --> <div class="form-group"> <label>产品名称</label> <input type="text" v-model="form.name" class="form-control" required> </div> <!-- FilePond 文件上传区域 --> <div class="form-group mt-4"> <label>产品图片</label> <file-pond ref="filePond" v-model="form.files" name="product_images" allow-multiple="true" allow-reorder="true" allow-file-remove="true" accepted-file-types="image/jpeg, image/png, image/webp" server="/api/filepond/upload" :headers="csrfHeaders" :onprocessfile="handleFileProcessed" :onremovefile="handleFileRemoved" label-idle="拖拽图片到这里或点击选择" /> </div> <button type="submit" class="btn btn-primary mt-4"> {{ isEdit ? '更新产品' : '创建产品' }} </button> </form> </div> </template> <script setup> import { ref, computed } from 'vue' import vueFilePond from 'vue-filepond' import 'filepond/dist/filepond.min.css' import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css' import FilePondPluginImagePreview from 'filepond-plugin-image-preview' import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type' import FilePondPluginReorder from 'filepond-plugin-reorder' // 注册FilePond组件及所需插件 const FilePond = vueFilePond( FilePondPluginImagePreview, FilePondPluginFileValidateType, FilePondPluginReorder ) // CSRF Token const csrfHeaders = computed(() => ({ 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') })) // 接收父组件传入的产品数据(编辑场景) const props = defineProps({ product: Object }) // 表单数据 const isEdit = computed(() => !!props.product) const form = ref({ name: props.product?.name || '', files: [] }) // 编辑场景:加载已有图片到FilePond if (isEdit.value && props.product.images) { form.value.files = props.product.images.map(img => ({ source: `/storage/${img.path}`, options: { type: 'local', metadata: { id: img.id } } })) } // 文件上传完成:保存后端返回的临时路径 const handleFileProcessed = (error, file) => { if (!error) { form.value.files = form.value.files.map(f => { if (f.id === file.id) { return { ...f, tempPath: file.serverId } } return f }) } } // 文件移除:若已上传到临时目录,调用后端删除 const handleFileRemoved = (error, file) => { if (!error && file.tempPath) { fetch(`/api/filepond/delete/${encodeURIComponent(file.tempPath)}`, { method: 'DELETE', headers: csrfHeaders.value }) } } // 提交表单 const handleSubmit = async () => { const payload = { name: form.value.name, tempImagePaths: form.value.files.filter(f => f.tempPath).map(f => f.tempPath), // 编辑场景:传递需删除的图片ID(若前端支持选中删除已有图片) deletedImageIds: form.value.files .filter(f => f.metadata?.id && !form.value.files.some(item => item.metadata?.id === f.metadata?.id)) .map(f => f.metadata.id) } try { if (isEdit.value) { await Inertia.put(`/products/${props.product.id}`, payload) } else { await Inertia.post('/products', payload) } Inertia.visit('/products') } catch (err) { console.error('提交失败:', err) } } </script>
功能对应说明
- 多文件上传:通过
allow-multiple="true"启用 - 预上传删除:
allow-file-remove="true"允许移除,handleFileRemoved处理已上传临时文件的删除 - 图片排序:引入
FilePondPluginReorder并启用allow-reorder="true",支持拖拽重排 - 加载已有图片:编辑场景下将后端返回的图片URL转换为FilePond支持的
local类型对象
后端Laravel实现
1. 路由配置(routes/api.php)
use App\Http\Controllers\FilepondController; use App\Http\Controllers\ProductController; // Filepond 临时文件处理 Route::post('/filepond/upload', [FilepondController::class, 'upload']); Route::delete('/filepond/delete/{path}', [FilepondController::class, 'delete']); // 产品CRUD Route::post('/products', [ProductController::class, 'store']); Route::put('/products/{product}', [ProductController::class, 'update']);
2. Filepond控制器(处理临时文件)
namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; class FilepondController extends Controller { // 上传临时文件 public function upload(Request $request) { $filepond = app('filepond'); $tempPath = $filepond->store($request->file('product_images')); return response()->json(['path' => $tempPath]); } // 删除临时文件 public function delete($path) { $filepond = app('filepond'); $filepond->delete($path); return response()->noContent(); } }
3. 产品控制器(关联图片与产品)
namespace App\Http\Controllers; use App\Models\Product; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; class ProductController extends Controller { // 创建产品并关联图片 public function store(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'tempImagePaths' => 'nullable|array' ]); $product = Product::create($validated); // 迁移临时文件到永久目录并关联产品 $this->processTempImages($product, $validated['tempImagePaths'] ?? []); return redirect()->route('products.index'); } // 更新产品及图片 public function update(Request $request, Product $product) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'tempImagePaths' => 'nullable|array', 'deletedImageIds' => 'nullable|array' ]); $product->update($validated); // 删除用户选择移除的已有图片 if (!empty($validated['deletedImageIds'])) { $product->images()->whereIn('id', $validated['deletedImageIds'])->each(function ($img) { Storage::delete("public/{$img->path}"); $img->delete(); }); } // 处理新上传的临时图片 $this->processTempImages($product, $validated['tempImagePaths'] ?? []); return redirect()->route('products.index'); } // 通用方法:迁移临时图片到永久目录 private function processTempImages(Product $product, array $tempPaths) { $destBasePath = "products/{$product->id}"; foreach ($tempPaths as $tempPath) { $fileName = basename($tempPath); $destPath = "{$destBasePath}/{$fileName}"; // 移动临时文件到永久存储 Storage::move("filepond/temp/{$tempPath}", "public/{$destPath}"); // 关联到产品 $product->images()->create([ 'path' => $destPath ]); // 删除临时文件(可选,调度任务会自动清理) Storage::delete("filepond/temp/{$tempPath}"); } } }
4. 数据库迁移
产品表迁移
Schema::create('products', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); });
产品图片表迁移
Schema::create('product_images', function (Blueprint $table) { $table->id(); $table->foreignId('product_id')->constrained()->onDelete('cascade'); $table->string('path'); // 存储相对路径,如products/1/image.jpg $table->timestamps(); });
模型关联(App/Models/Product.php)
public function images() { return $this->hasMany(ProductImage::class); }
临时文件自动清理
通过Laravel任务调度定期清理过期临时文件:
- 在
app/Console/Kernel.php中添加调度:
use Illuminate\Console\Scheduling\Schedule; use League\Flysystem\Filesystem; use League\Flysystem\Local\LocalFilesystemAdapter; protected function schedule(Schedule $schedule) { // 每天凌晨2点清理24小时前的临时文件 $schedule->call(function () { $adapter = new LocalFilesystemAdapter(storage_path('app/filepond/temp')); $filesystem = new Filesystem($adapter); // 删除过期文件 $filesystem->listContents('', true) ->filter(fn($item) => $item['type'] === 'file' && now()->subHours(24)->gt($item['timestamp'])) ->each(fn($item) => $filesystem->delete($item['path'])); // 清理空文件夹 $filesystem->listContents('', true) ->filter(fn($item) => $item['type'] === 'dir' && empty($filesystem->listContents($item['path']))) ->each(fn($item) => $filesystem->deleteDirectory($item['path'])); })->dailyAt('02:00'); }
- 配置服务器定时任务(如Cron):
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
内容的提问来源于stack exchange,提问作者Stef
相关产品推荐
相关产品推荐

