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

基于Vue、Inertia、Laravel,如何在编辑组件中更新文件对象?

问题描述

我有一个行程编辑页面,修改内容后调用update函数更新Itinerary对象,遇到以下异常情况:

  • 直接点击提交,ItineraryController中用dd($request)能正常返回所有现有表单数据;
  • 修改字段内容后提交,也能成功返回完整请求对象;
  • 但在「替换文件」选择器中选择文件后提交,整个请求对象显示为null,导致无法提交。

需要调整实现「替换文件」输入功能正常,且提交时请求对象包含原有行程数据。


组件代码(Vue3 + Inertia)

<template>
  <form @submit.prevent="submit">
    <div class="row w-75 m-auto">
      <h1>Edit Itinerary</h1>
      <div class="col-md-6">
        <label for="title">Title</label>
        <input v-model="form.title" class="form-control" name="title" placeholder="Itinerary Title" type="text" />
      </div>
    </div>
    <div class="row w-75 m-auto">
      <div class="col-md-6">
        <label for="gen_narrative">Narrative</label>
        <textarea v-model="form.gen_narrative" class="form-control" name="gen_narrative" placeholder="Itinerary Narrative"></textarea>
      </div>
    </div>
    <div class="row w-75 m-auto">
      <div class="col-md-6">
        <label>Current Photo</label>
        <img :src="assetUrl(props.itinerary.f_photo)" alt="featured photo" />
      </div>
    </div>
    <br />
    <div class="row w-75 m-auto">
      <div class="col-md-6">
        <label for="f_photo">Replace Photo</label>
        <input class="form-control" name="f_photo" type="file" @input="fileChange" />
      </div>
    </div>
    <div class="row w-75 m-auto">
      <div class="col-md-6">
        <label for="authorid">Author Name</label>
        <input v-model="form.authorid" class="form-control" name="authorid" placeholder="Author Name" type="text" />
      </div>
    </div>
    <div class="row w-75 m-auto">
      <div class="col-md-6">
        <button class="btn btn-primary" type="submit">Edit Itinerary</button>
      </div>
    </div>
  </form>
</template>

<script setup>
import { useForm } from "@inertiajs/inertia-vue3";

function assetUrl(path) {
  return process.env.MIX_BASE_URL + "storage/" + path;
}

function fileChange(event) {
  form.f_photo = event.target.files[0];
}

let props = defineProps({
  itinerary: {
    type: Object,
    required: true,
  },
});

let form = useForm({
  title: props.itinerary.title,
  gen_narrative: props.itinerary.gen_narrative,
  f_photo: null,
  authorid: props.itinerary.authorid,
  stops: props.itinerary.stops,
});
let submit = () => {
  console.log(form.title);
  form.patch("/itineraries/" + props.itinerary.id);
  form.reset();
};
console.log(form);
</script>

<style scoped></style>

控制器代码(Laravel)

public function edit($id)
{
    $itinerary = Itinerary::find($id);
    return Inertia::render('Itineraries/Edit', [
        'itinerary' => $itinerary,
    ]);
}

public function update(Request $request, $id)
{
    $itinerary = Itinerary::find($id);
    $itinerary->title = $request->title;
    $itinerary->gen_narrative = $request->gen_narrative;
    //upload the photo
    if ($request->hasFile('f_photo')) {
        $itinerary->f_photo = $request->f_photo->store('public/itinerary_photos');
    }
    $itinerary->authorid = $request->authorid;
    $itinerary->save();

    return redirect('/itineraries');
}

解决方案

问题根源

当表单包含文件上传时,Inertia默认的JSON请求格式无法传输二进制文件,必须改用multipart/form-data格式提交,否则后端无法解析请求,导致返回null。


1. 前端组件修改

核心调整点:

  • 用FormData处理文件上传,通过Inertia的transform方法转换请求数据格式
  • 避免提前重置表单,确保提交完成后再清空数据

修改后的<script setup>代码:

<script setup>
import { useForm } from "@inertiajs/inertia-vue3";

function assetUrl(path) {
  return process.env.MIX_BASE_URL + "storage/" + path;
}

function fileChange(event) {
  form.f_photo = event.target.files[0];
}

let props = defineProps({
  itinerary: {
    type: Object,
    required: true,
  },
});

let form = useForm({
  title: props.itinerary.title,
  gen_narrative: props.itinerary.gen_narrative,
  f_photo: null,
  authorid: props.itinerary.authorid,
  stops: props.itinerary.stops,
});

// 转换表单数据为FormData,支持文件上传
form.transform((data) => {
  const formData = new FormData();
  Object.keys(data).forEach(key => {
    if (data[key] !== null) {
      formData.append(key, data[key]);
    }
  });
  return formData;
});

let submit = () => {
  console.log(form.title);
  // 提交完成后再重置表单,避免提前清空数据
  form.patch("/itineraries/" + props.itinerary.id)
    .then(() => {
      form.reset();
    });
};
</script>

额外可选:给form标签添加enctype="multipart/form-data"属性,增强兼容性:

<form @submit.prevent="submit" enctype="multipart/form-data">

2. 后端控制器优化

核心调整点:

  • 用findOrFail替代find,避免找不到模型时抛出错误
  • 添加表单验证,确保数据合法性
  • 优化文件上传逻辑(可选:删除旧文件)

修改后的控制器代码:

use Illuminate\Support\Facades\Storage;

public function update(Request $request, $id)
{
    $itinerary = Itinerary::findOrFail($id);
    
    // 表单验证
    $validated = $request->validate([
        'title' => 'required|string',
        'gen_narrative' => 'nullable|string',
        'f_photo' => 'nullable|image|max:2048', // 限制图片类型和大小(2MB)
        'authorid' => 'required|string',
    ]);

    $itinerary->title = $validated['title'];
    $itinerary->gen_narrative = $validated['gen_narrative'] ?? $itinerary->gen_narrative;
    $itinerary->authorid = $validated['authorid'];

    // 处理文件上传(可选:删除旧文件)
    if ($request->hasFile('f_photo')) {
        if ($itinerary->f_photo) {
            Storage::delete($itinerary->f_photo);
        }
        $itinerary->f_photo = $request->file('f_photo')->store('public/itinerary_photos');
    }

    $itinerary->save();

    return redirect('/itineraries');
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 00:30:21