Full Vue 3 + Element Plus Component: Layout, Table, Pagination, Forms, Dialogs, Breadcrumbs, Skeleton, Upload & Tree Menu

This article provides a complete Vue 3 single‑file component using Element Plus that demonstrates a unified layout container with breadcrumbs, a searchable table, pagination, form validation, dialogs, file upload, and a tree‑style menu, all ready to copy and use without configuration.

liandk
liandk
liandk
Full Vue 3 + Element Plus Component: Layout, Table, Pagination, Forms, Dialogs, Breadcrumbs, Skeleton, Upload & Tree Menu

Overview

The article presents a ready‑to‑use Vue 3 single‑file component built with Element Plus. It combines a standard backend‑admin page layout—including breadcrumbs, a search form, a data table with status tags, pagination, a dialog with form validation, an upload button, and a tree‑style menu—into one cohesive component.

Template Structure

<template>
  <div class="app-container">
    <!-- 1. Breadcrumb -->
    <el-breadcrumb class="mb-4">
      <el-breadcrumb-item>首页</el-breadcrumb-item>
      <el-breadcrumb-item>系统管理</el-breadcrumb-item>
      <el-breadcrumb-item>用户列表</el-breadcrumb-item>
    </el-breadcrumb>

    <!-- 2. Top search form -->
    <div class="search-form">
      <el-form :model="queryForm" inline @submit.prevent="handleSearch">
        <el-form-item label="用户名">
          <el-input v-model="queryForm.username" placeholder="请输入用户名" style="width: 200px" />
        </el-form-item>
        <el-form-item label="状态">
          <el-select v-model="queryForm.status" placeholder="请选择" style="width: 120px">
            <el-option label="启用" value="1" />
            <el-option label="禁用" value="0" />
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleSearch">搜索</el-button>
          <el-button @click="handleReset">重置</el-button>
          <el-button type="success" @click="openAddDialog">新增</el-button>
        </el-form-item>
      </el-form>
    </div>

    <!-- 3. Skeleton (loading) -->
    <el-skeleton v-if="loading" rows="8" animated class="mb-4" />

    <!-- 4. Data table (when not loading) -->
    <el-table v-else :data="tableData" border stripe style="width: 100%" class="mb-4">
      <el-table-column prop="id" label="ID" align="center" />
      <el-table-column prop="username" label="用户名" align="center" />
      <el-table-column prop="phone" label="手机号" align="center" />
      <el-table-column prop="status" label="状态" align="center">
        <template #default="scope">
          <el-tag :type="scope.row.status === 1 ? 'success' : 'danger'">
            {{ scope.row.status === 1 ? '启用' : '禁用' }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center">
        <template #default="scope">
          <el-button type="primary" size="small" @click="openEditDialog(scope.row)">编辑</el-button>
          <el-button type="danger" size="small" @click="handleDelete(scope.row.id)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 5. Pagination -->
    <el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :total="total"
      layout="total, sizes, prev, pager, next, jumper"
      @size-change="handlePageChange" @current-change="handlePageChange"
      class="text-right" />

    <!-- 6. Add/Edit dialog with validation -->
    <el-dialog v-model="dialogVisible" title="用户信息" width="500px">
      <el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
        <el-form-item label="用户名" prop="username">
          <el-input v-model="formData.username" placeholder="请输入用户名" />
        </el-form-item>
        <el-form-item label="手机号" prop="phone">
          <el-input v-model="formData.phone" placeholder="请输入手机号" />
        </el-form-item>
        <el-form-item label="状态" prop="status">
          <el-radio-group v-model="formData.status">
            <el-radio :label="1">启用</el-radio>
            <el-radio :label="0">禁用</el-radio>
          </el-radio-group>
        </el-form-item>
        <!-- 7. Upload component -->
        <el-form-item label="头像上传">
          <el-upload action="https://jsonplaceholder.typicode.com/posts/" :show-file-list="false" :on-success="handleUploadSuccess">
            <el-button type="info">点击上传头像</el-button>
          </el-upload>
        </el-form-item>
        <!-- 8. Tree menu -->
        <el-form-item label="角色权限">
          <el-tree v-model="checkedKeys" :data="treeData" show-checkbox node-key="id" default-expand-all />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="handleSubmit">确认提交</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'

// Pagination state
const pageNum = ref(1)
const pageSize = ref(10)
const total = ref(100)

// Loading flag
const loading = ref(true)

// Search form model
const queryForm = reactive({ username: '', status: '' })

// Table data
const tableData = ref([])

// Dialog control
const dialogVisible = ref(false)
const formRef = ref(null)

// Form data and validation rules
const formData = reactive({ id: '', username: '', phone: '', status: 1 })
const formRules = {
  username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
  phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
  status: [{ required: true, message: '请选择状态', trigger: 'change' }]
}

// Tree data
const treeData = ref([
  { id: 1, label: '系统管理', children: [
    { id: 2, label: '用户管理' },
    { id: 3, label: '角色管理' }
  ]}
])
const checkedKeys = ref([])

// Simulated API request
onMounted(() => {
  setTimeout(() => {
    tableData.value = Array.from({ length: 10 }).map((_, i) => ({
      id: i + 1,
      username: '用户' + (i + 1),
      phone: '13800138000',
      status: i % 2
    }))
    loading.value = false
  }, 800)
})

// Search handler
const handleSearch = () => {
  loading.value = true
  setTimeout(() => {
    loading.value = false
    ElMessage.success('搜索成功')
  }, 300)
}

// Reset handler
const handleReset = () => {
  queryForm.username = ''
  queryForm.status = ''
}

// Pagination change handler
const handlePageChange = () => {
  loading.value = true
  setTimeout(() => loading.value = false, 300)
}

// Open add dialog
const openAddDialog = () => {
  dialogVisible.value = true
  formData.id = ''
  formData.username = ''
  formData.phone = ''
  formData.status = 1
}

// Open edit dialog
const openEditDialog = (row) => {
  dialogVisible.value = true
  formData.id = row.id
  formData.username = row.username
  formData.phone = row.phone
  formData.status = row.status
}

// Delete handler
const handleDelete = (id) => {
  ElMessage.success(`删除 ID: ${id} 成功`)
}

// Submit form
const handleSubmit = async () => {
  await formRef.value.validate()
  ElMessage.success('提交成功')
  dialogVisible.value = false
}

// Upload success callback
const handleUploadSuccess = () => {
  ElMessage.success('上传成功')
}
</script>

<style scoped>
.app-container {
  padding: 20px;
  background: #fff;
  min-height: calc(100vh - 60px);
}
.text-right { text-align: right; }
.mb-4 { margin-bottom: 16px; }
.search-form {
  padding: 12px;
  background: #f9fafb;
  border-radius: 4px;
  margin-bottom: 16px;
}
</style>

Component Features

Unified layout container suitable for enterprise back‑office pages.

Breadcrumb navigation for hierarchical context.

Inline search form with username and status filters.

Skeleton screen displayed while data is loading.

Data table with ID, username, phone, and status columns; status shown with colored tags.

Action column providing edit and delete buttons.

Pagination component supporting page size change and quick jumps.

Dialog containing a form with required field validation for username, phone, and status.

File upload button that posts to a placeholder endpoint and shows a success message.

Tree menu with checkboxes for role‑based permission selection.

Benefits

The component can be copied directly into a Vue 3 project and used without any additional configuration, offering a consistent UI, built‑in loading states, validation, and simulated API interactions, which accelerates development of standard admin pages.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

LayoutPaginationSingle File ComponentVue 3Form ValidationElement PlusTableTree Menu
liandk
Written by

liandk

Seasoned Java and mobile developer with years of experience, specializing in mini‑programs, public accounts, and full‑stack front‑end development. In the AI era, I continuously learn to broaden my knowledge and evolve. I revived a public account I started a decade ago during a dessert‑startup venture, using code as a vessel and knowledge as a companion. I share personal projects, technical articles, programming tips, and growth insights—let’s improve together and set sail.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.