当前位置:首页 > 工具 > 正文内容

Vue 3 与 Element Plus 组件开发实战案例解析

访客 工具 2026年7月8日 2

1. 基于 Element Plus 的动态表单校验实战

在 Vue 3 中结合 Element Plus 实现表单开发时,通常使用响应式对象来管理表单状态与校验规则。以下是一个订单信息录入的示例,展示了基础校验与重置逻辑。

<template>
  <el-form :model="orderForm" :rules="validationRules" ref="formRef" label-width="120px">
    <el-form-item label="项目名称" prop="title">
      <el-input v-model="orderForm.title" placeholder="请输入项目名称" />
    </el-form-item>
    <el-form-item label="所属区域" prop="location">
      <el-select v-model="orderForm.location" placeholder="请选择区域">
        <el-option label="华东" value="east" />
        <el-option label="华北" value="north" />
        <el-option label="华南" value="south" />
      </el-select>
    </el-form-item>
    <el-form-item label="截止日期" prop="deadline">
      <el-date-picker v-model="orderForm.deadline" type="date" placeholder="选择日期" />
    </el-form-item>
    <el-form-item label="紧急程度" prop="priority">
      <el-radio-group v-model="orderForm.priority">
        <el-radio label="高" />
        <el-radio label="普通" />
      </el-radio-group>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="handleSave(formRef)">确认保存</el-button>
      <el-button @click="resetFields(formRef)">重置</el-button>
    </el-form-item>
  </el-form>
</template>

<script setup>
import { reactive, ref } from 'vue'

const formRef = ref()
const orderForm = reactive({
  title: '',
  location: '',
  deadline: '',
  priority: '普通'
})

const validationRules = {
  title: [
    { required: true, message: '名称不能为空', trigger: 'blur' },
    { min: 5, max: 20, message: '字数需在5到20之间', trigger: 'blur' }
  ],
  location: [{ required: true, message: '请选择区域', trigger: 'change' }]
}

const handleSave = async (el) => {
  if (!el) return
  await el.validate((isValid) => {
    if (isValid) {
      console.log('提交成功:', orderForm)
    } else {
      console.warn('验证未通过')
    }
  })
}

const resetFields = (el) => {
  if (el) el.resetFields()
}
</script>

常见问题排查:必填项校验不消失

在使用 Element Plus 表单时,若遇到数据已填但校验提示不消失的情况,通常由以下原因导致:

  1. 字段未定义:reactiveref 定义的表单对象中,必须声明与 prop 对应的属性键。
  2. 触发方式不匹配:建议将 trigger 设置为 ['blur', 'change'] 以确保在失去焦点和值变动时均能触发校验。

2. 幻灯片组件(Carousel)应用

轮播图是常见的 UI 组件。在 Vue 3 中,我们可以通过 v-for 动态渲染图片列表。

<template>
  <div class="slider-container">
    <el-carousel height="400px" indicator-position="outside">
      <el-carousel-item v-for="(src, index) in bannerList" :key="index">
        <img :src="src" class="banner-img" style="width:100%; height:100%" />
      </el-carousel-item>
    </el-carousel>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const bannerList = ref([
  'https://via.placeholder.com/800x400/FF5733/FFFFFF',
  'https://via.placeholder.com/800x400/33FF57/FFFFFF',
  'https://via.placeholder.com/800x400/3357FF/FFFFFF'
])
</script>

3. Teleport 技术实现全屏模态框

Teleport 是 Vue 3 的核心特性,它允许我们将组件挂载到 DOM 树中 #app 之外的任意位置(如 body),非常适合处理弹窗覆盖问题。

<template>
  <div class="container">
    <el-button @click="isModalOpen = true">激活传送门弹窗</el-button>

    <teleport to="body">
      <div v-if="isModalOpen" class="overlay-mask">
        <div class="modal-content">
          <h3>系统提示</h3>
          <p>此弹窗已传送至 body 根节点下。</p>
          <el-button @click="isModalOpen = false">关闭</el-button>
        </div>
      </div>
    </teleport>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const isModalOpen = ref(false)
</script>

<style scoped>
.overlay-mask {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.6);
  display: flex; justify-content: center; align-items: center;
}
.modal-content {
  background: #fff; padding: 30px; border-radius: 8px; text-align: center;
}
</style>

4. 任务列表(To-do List)简易应用

利用 Vue 3 的响应式 API,可以快速构建一个具有增删功能的任务清单。

<div id="todo-app">
  <h2>{{ headerText }}</h2>
  <div>
    <input v-model="inputTask" placeholder="输入待办事项" />
    <button @click="pushTask">添加</button>
  </div>
  <ul>
    <li v-for="task in tasks" :key="task.id">
      {{ task.content }}
      <button @click="removeTask(task.id)">移除</button>
    </li>
  </ul>
</div>

<script>
const { createApp, ref, reactive } = Vue;
createApp({
  setup() {
    const headerText = ref('我的日程表');
    const inputTask = ref('');
    const tasks = reactive([
      { id: Date.now(), content: '早起跑步' }
    ]);

    const pushTask = () => {
      if (!inputTask.value) return;
      tasks.push({ id: Date.now(), content: inputTask.value });
      inputTask.value = '';
    };

    const removeTask = (id) => {
      const idx = tasks.findIndex(t => t.id === id);
      if (idx !== -1) tasks.splice(idx, 1);
    };

    return { headerText, inputTask, tasks, pushTask, removeTask };
  }
}).mount('#todo-app');
</script>

5. 电影院选座逻辑实现

选座功能涉及复杂的数组状态管理。通过计算属性和 v-bind:class,可以实现座位状态的动态切换。

<script setup>
import { reactive, computed, toRefs } from 'vue'

const state = reactive({
  selectedSeats: [],
  rows: 10,
  cols: 10,
  // 0: 可选, 1: 已选, 2: 维修, -1: 空地
  seatMap: [
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 2, 2, 0, 0, 0, 0, 2, 2, 0,
    0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
    // ... 更多数据
  ],
  price: 45
})

const totalPrice = computed(() => state.selectedSeats.length * state.price)

const toggleSeat = (index) => {
  if (state.seatMap[index] === 0) {
    if (state.selectedSeats.length >= 5) return alert('最多选5个座位')
    state.seatMap[index] = 1
    state.selectedSeats.push(index)
  } else if (state.seatMap[index] === 1) {
    state.seatMap[index] = 0
    state.selectedSeats = state.selectedSeats.filter(i => i !== index)
  }
}
</script>

6. 组件间的数据穿透:Provide/Inject

当层级较深时,使用 ProvideInject 可以避免繁琐的 Prop 逐层传递。为了安全起见,通常配合 readonly 使用。

// 父组件 Parent.vue
<script setup>
import { provide, ref, readonly } from 'vue'
import ChildComponent from './Child.vue'

const configInfo = ref({ theme: 'dark', lang: 'zh-cn' })
provide('appConfig', readonly(configInfo))
</script>

// 深层子组件 Child.vue
<script setup>
import { inject } from 'vue'
const config = inject('appConfig', { theme: 'light' }) // 提供默认值
</script>
<template>
  <div>当前主题: {{ config.theme }}</div>
</template>

7. 购物车全选与金额计算逻辑

在购物车场景中,computedgetset 方法是实现"全选/反选"逻辑的最佳实践,同时使用 reduce 进行金额汇总。

<script setup>
import { reactive, computed } from 'vue'

const cartItems = reactive([
  { id: 1, name: '机械键盘', price: 299, count: 1, checked: true },
  { id: 2, name: '电竞鼠标', price: 159, count: 2, checked: false }
])

const isAllChecked = computed({
  get: () => cartItems.every(item => item.checked),
  set: (val) => cartItems.forEach(item => item.checked = val)
})

const totalAmount = computed(() => {
  return cartItems
    .filter(item => item.checked)
    .reduce((sum, item) => sum + item.price * item.count, 0)
})
</script>

8. 数据表格的分页与行编辑

Element Plus 的 el-table 配合 el-pagination 可实现高效的数据展示。通过 v-model:current-page 实现双向绑定。

<template>
  <el-table :data="pagedData" border>
    <el-table-column prop="id" label="ID" width="100" />
    <el-table-column prop="userName" label="用户名" />
    <el-table-column label="操作">
      <template #default="{ row }">
        <el-button size="small" @click="editRow(row)">修改</el-button>
        <el-button size="small" type="danger" @click="deleteRow(row)">注销</el-button>
      </template>
    </el-table-column>
  </el-table>

  <el-pagination
    layout="prev, pager, next"
    :total="allUsers.length"
    :page-size="pageSize"
    v-model:current-page="pageIndex"
  />
</template>

<script setup>
import { ref, computed } from 'vue'

const allUsers = ref([/* 大量用户数据 */])
const pageIndex = ref(1)
const pageSize = ref(10)

const pagedData = computed(() => {
  const start = (pageIndex.value - 1) * pageSize.value
  return allUsers.value.slice(start, start + pageSize.value)
})
</script>

9. 后台管理系统侧边菜单布局

侧边菜单通常由 el-menu 构成,配合 el-icon 增强视觉引导。可以使用嵌套循环处理二级或三级菜单。

<template>
  <el-menu
    class="sidebar-menu"
    :collapse="collapsed"
    background-color="#304156"
    text-color="#bfcbd9"
  >
    <el-sub-menu v-for="menu in menuData" :key="menu.path" :index="menu.path">
      <template #title>
        <el-icon><component :is="menu.icon" /></el-icon>
        <span>{{ menu.name }}</span>
      </template>
      <el-menu-item v-for="sub in menu.children" :key="sub.path" :index="sub.path">
        {{ sub.name }}
      </el-menu-item>
    </el-sub-menu>
  </el-menu>
</template>

<script setup>
import { ref } from 'vue'
const collapsed = ref(false)
const menuData = [
  { name: '用户管理', icon: 'User', path: '/users', children: [
    { name: '角色权限', path: '/roles' }
  ]},
  { name: '系统监控', icon: 'Odometer', path: '/monitor', children: [
    { name: '性能统计', path: '/stats' }
  ]}
]
</script>

相关文章

Tailscale 的详细用法

Tailscale 是一种基于 WireGuard 协议 的 零配置 VPN(虚拟私有网络)服务,让设备之间能够 安全、加密地直接连接,就像它们在同一个本地网络一样。它的核心特点是 简单、安全、跨平台。Tailscale 非常适合 没有公网 IP、两台电脑不在同一局域网 的场景。 简单来说,Tailscale 是什么?Tailscale 是一款让你的各种设备(电脑、服务器、手机...

Clash Tun 模式 导致 爱快(iKuai SD-Wan)内网域名无法访问

一、Clash  DNS 配置dns:  enable: true  listen: 0.0.0.0:53  ipv6: true  enhanced-mode: redir-host  nameserver:    - 223.5.5.5    - 223.6.6.6iKuai 内网域名 ...

深入解析Node.js运行环境与异步I/O架构

深入解析Node.js运行环境与异步I/O架构

核心定义与价值Node.js本质上是一个JavaScript运行环境,而非编程语言或应用框架。它赋予了JavaScript脱离浏览器在服务端、命令行工具及网络应用中执行的能力。其核心意义在于:用单一语言打通前后端开发壁垒。基于事件驱动与非阻塞I/O的架构特性,Node.js在处理API网关、实时通信及微服务等I/O密集型场景时表现卓越,已成为现代后端工程的主流选择。浏览器沙箱限制1995年Java...

ADO.NET SQL参数化查询的最佳实践

在 ADO.NET 中执行 SQL 查询时,参数化查询是一种关键的安全措施和性能优化手段。它通过将 SQL 命令和用户提供的数据分开处理,有效防止了 SQL 注入攻击,并有助于数据库缓存执行计划。下面总结了几种常用的参数化查询方式。 1. 使用 SqlParameter 对象(推荐) 这是最推荐的参数化查询方式。通过显式创建 SqlParameter 对象,您可以精确控制参数的类...

基于ELK的日志集中化分析系统搭建

构建统一日志管理平台的必要性 在分布式架构中,各服务节点独立运行,日志分散存储于不同主机。传统通过命令行工具如grep、awk逐个检索日志的方式,在数据量庞大时效率极低,难以实现快速定位问题。为提升运维效率,需建立集中式日志处理体系,具备日志采集、传输、存储、分析与告警能力。 ELK技术栈核心组件解析 Elasticsearch:分布式搜索引擎,支持全文检索、实时数据分析和高可用集群部署,...

企业级 Oracle 数据库部署与初始化实战

系统环境规划与前置条件检查 在着手部署 Oracle RDBMS 之前,必须对底层基础设施进行严格评估。该企业级关系型数据库管理系统常用于处理高并发业务逻辑,因此对计算资源、存储 I/O 及网络稳定性有较高标准。 1. 资源规格定义 为确保实例稳定运行,建议满足以下基准配置: 计算单元:双核或以上处理器,主频不低于 2.0GHz。 内存容量:物理内存至少 2GB,生产环境建议扩容至 4GB 以...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。