You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求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任务调度定期清理过期临时文件:

  1. 在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');
}
  1. 配置服务器定时任务(如Cron):
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

内容的提问来源于stack exchange,提问作者Stef

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 22:14:56