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

基于Laravel+Vue+Inertia实现用户多角色多选功能(无第三方包)

Laravel + Vue + Inertia 实现用户多角色选择(无第三方包方案)

问题背景

我是Laravel新手,正在开发一个项目,需要实现多选功能让用户选择多个角色。数据库中users表与roles表为多对多关系,使用Laravel、VueJS和InertiaJS开发。已创建数据库表(users、roles、role_user)及对应模型,但不清楚如何将选中角色与数据库实体关联,以及如何为用户选择多角色,寻求不使用第三方包的实现方案。现有代码如下:

现有Create.vue代码

<script setup>
import BreezeAuthenticatedLayout from '@/Layouts/Authenticated.vue';
import Button from '@/Components/Button.vue'
import { Head } from '@inertiajs/inertia-vue3';
import { useForm, Link } from '@inertiajs/inertia-vue3';
import InputIconWrapper from '@/Components/InputIconWrapper.vue'
import Input from '@/Components/Input.vue'
import Label from '@/Components/Label.vue'
import Multiselect from '@vueform/multiselect'
defineProps({ errors: Object })
const data = useForm({
        name: '',


email: '',


password: '',
  password_confirmation: '',
    });
const submit = ()=> {
       data.post("/users");
    }
</script>

 <form @submit.prevent="submit">

          <div>
            <Label for="name" value="Name">Name</Label>
            <Input
              type="text"
              placeholder="Name"
              v-model="data.name"
              required autofocus autocomplete="name" 
              class="block w-full w-full px-4 py-2 mt-2 mb-2 border rounded-md focus:outline-none focus:ring-1 focus:ring-blue-600"
            />
            <div v-if="errors.name" class="text-red-500">{{ errors.name}}</div>
          </div>
          <div>
            <Label for="email" value="Email">Email</Label>
            <Input
              type="email"
              placeholder="Email"
              v-model="data.email"
              required autofocus autocomplete="email" 
              class="block w-full w-full px-4 py-2 mt-2 mb-2 border rounded-md focus:outline-none focus:ring-1 focus:ring-blue-600"
            />
            <div v-if="errors.email" class="text-red-500">{{ errors.email}}</div>
          </div>
          <div>
            <Label for="password" value="Password">Password</Label>
            <Input
              type="password"
              placeholder="Password"
              v-model="data.password"
              required autofocus autocomplete="password" 
              class="block w-full w-full px-4 py-2 mt-2 mb-2 border rounded-md focus:outline-none focus:ring-1 focus:ring-blue-600"
            />
            
            <div v-if="errors.password" class="text-red-500">{{ errors.password }}</div>
          </div>
          <div>
            <Label for="password_confirmation">Confirm Password</Label>
            <Input
              type="password"
              placeholder="Password"
              v-model="data.password_confirmation"
              class="block w-full w-full px-4 py-2 mt-2 mb-2 border rounded-md focus:outline-none focus:ring-1 focus:ring-blue-600"
            />
            
            <div v-if="errors.password_confirmation" class="text-red-500">{{ errors.password_confirmation }}</div>
          </div>
          <div>
            <label for="role">Role</label>
            <multiselect
  v-model="selectedRoles"
  :options="roles"
  :multiple="true"
  placeholder="Select roles"
></multiselect>
            <div v-if="errors.password_confirmation" class="text-red-500">{{ errors.password_confirmation }}</div>
          </div>
          <div class="flex items-center mt-4">
            <Button type="submit" variant="success" size="base" class="mr-2"  >
                Add User
    </Button>

    <Button variant="danger" size="base" class="mr-2"><Link :href="route('users.index')" >
              Cancel</Link>
         
    </Button>

          </div>
          
        </form>

<script>
import axios from 'axios';
import Multiselect from '@vueform/multiselect';

export default {
  components: {
    Multiselect,
  },
  data() {
    return {
      roles: [],
      selectedRoles: [],
    };
  },
  mounted() {
    this.fetchRoles();
  },
  methods: {
    fetchRoles() {
      axios.get('/api/roles')
        .then(response => {
          this.roles = response.data;
        })
        .catch(error => {
          console.error(error);
        });
    },
  },
};
</script>

<style src="@vueform/multiselect/themes/default.css"></style>

现有User Controller代码

public function create()
{
    return inertia('Users/Create');
}

public function store(StoreUserRequest $request)
{
    $validatedData = $request->all();

    $validatedData['password'] = Hash::make($validatedData['password']);

   User::create($validatedData);

   return Redirect::route('users.index')
            ->with('message', 'User created successfully');
}

解决方案步骤

1. 前端Vue组件调整

当前代码的核心问题是selectedRoles未与Inertia的表单数据绑定,导致提交时不会传递角色信息。以下提供两种实现方案:

方案1:修正现有第三方多选组件代码

<script setup>
import BreezeAuthenticatedLayout from '@/Layouts/Authenticated.vue';
import Button from '@/Components/Button.vue'
import { Head } from '@inertiajs/inertia-vue3';
import { useForm, Link } from '@inertiajs/inertia-vue3';
import Input from '@/Components/Input.vue'
import Label from '@/Components/Label.vue'
import Multiselect from '@vueform/multiselect'

// 接收后端传递的角色列表
defineProps({ 
  errors: Object,
  roles: Array
})

// 将选中角色整合到表单数据中
const data = useForm({
  name: '',
  email: '',
  password: '',
  password_confirmation: '',
  selectedRoles: []
});

const submit = ()=> {
  data.post("/users");
}
</script>

<template>
  <form @submit.prevent="submit">
    <!-- 原有姓名、邮箱、密码字段保持不变 -->
    
    <div>
      <Label for="role" value="角色"></Label>
      <multiselect
        v-model="data.selectedRoles"
        :options="roles"
        :multiple="true"
        placeholder="选择角色"
        track-by="id"
        label="name"
      ></multiselect>
      <!-- 修正错误提示指向角色字段 -->
      <div v-if="errors.selectedRoles" class="text-red-500">{{ errors.selectedRoles}}</div>
    </div>

    <!-- 原有提交按钮部分保持不变 -->
  </form>
</template>

<style src="@vueform/multiselect/themes/default.css"></style>

方案2:无第三方包的原生多选实现

如果不想依赖第三方组件,直接用原生<select multiple>:

<!-- 替换第三方多选组件部分 -->
<div>
  <Label for="roles" value="角色"></Label>
  <select 
    v-model="data.selectedRoles" 
    multiple 
    class="block w-full px-4 py-2 mt-2 mb-2 border rounded-md focus:outline-none focus:ring-1 focus:ring-blue-600"
  >
    <option v-for="role in roles" :key="role.id" :value="role.id">{{ role.name }}</option>
  </select>
  <div v-if="errors.selectedRoles" class="text-red-500">{{ errors.selectedRoles}}</div>
</div>

2. 后端控制器与验证调整

修改UserController

use App\Models\Role;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Redirect;

public function create()
{
    // 直接从后端传递角色列表给前端,避免额外请求
    return inertia('Users/Create', [
        'roles' => Role::select('id', 'name')->get()
    ]);
}

public function store(StoreUserRequest $request)
{
    $validatedData = $request->validated();
    $validatedData['password'] = Hash::make($validatedData['password']);

    // 创建用户实例
    $user = User::create($validatedData);

    // 关联选中的角色
    if (!empty($request->selectedRoles)) {
        $user->roles()->attach($request->selectedRoles);
    }

    return Redirect::route('users.index')
        ->with('message', '用户创建成功');
}

添加请求验证规则

在app/Http/Requests/StoreUserRequest.php中补充角色字段的验证:

public function rules()
{
    return [
        'name' => 'required|string|max:255',
        'email' => 'required|string|email|max:255|unique:users',
        'password' => 'required|string|confirmed|min:8',
        'selectedRoles' => 'nullable|array',
        'selectedRoles.*' => 'exists:roles,id' // 确保选中的角色ID在数据库中存在
    ];
}

3. 确认模型多对多关联

确保User和Role模型已正确定义关联:

User.php

public function roles()
{
    return $this->belongsToMany(Role::class);
}

Role.php

public function users()
{
    return $this->belongsToMany(User::class);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 11:59:54