前端实战】Vue3+Element Plus打造企业级后台管理系统:从零到部署

RuoYi-Vue3项目实战:从零搭建企业级后台管理系统(Vite+Element Plus版) 本文详细介绍了基于Vue3和Vite的RuoYi-Vue3企业级后台管理系统从零开始的实战搭建过程。内容涵盖项目初始化、环境配置、核心架构解析、工程化实践,并以用户管理模块为例,演示了完整的API对接、页面构建与表单验证流程。同时,深入探讨了权限模型、性能优化、主题定制以及生产部署等高级主题,旨在帮助开发者高效构建和维护符合自身业务需求的后台应用。 阅读详情

1. 为什么选择Vue3和Element Plus来构建企业级后台?

如果你正在为团队或公司搭建一个后台管理系统,或者想从零开始学习如何构建一个完整的、能上线的项目,那么你找对地方了。我做过不少这类项目,从早期的jQuery时代到后来的Vue 2,再到现在的Vue 3,一路踩坑过来,最大的感受就是:选对技术栈,真的能省下一半的头发。今天,我就来跟你聊聊,为什么Vue 3加上Element Plus,是目前搭建企业级后台最舒服、最高效的组合之一。

首先说说Vue 3。你可能听说过它性能好、体积小,但这到底意味着什么?我举个实际的例子。以前用Vue 2做一个稍微复杂点的数据看板,页面里图表、表格、筛选器一大堆,数据一更新,有时候能感觉到轻微的卡顿。后来重构到Vue 3,最直观的感受就是“跟手”了。这背后是Vue 3全新的响应式系统(用的是Proxy)和编译时优化在起作用。对于后台管理系统这种交互复杂、组件繁多、状态管理要求高的应用,这种性能提升带来的体验改善是实实在在的。更重要的是它的组合式API,这玩意儿简直是管理复杂业务逻辑的“神器”。以前用选项式API,一个功能相关的data、methods、computed、watch散落在文件各处,维护起来像在玩“找不同”。现在用<script setup>配合组合式函数,可以把一个功能的所有代码聚合在一起,复用起来也极其方便。比如一个复杂的用户权限判断逻辑,我封装成一个usePermission的组合式函数,在任何页面里都是一行import的事,清晰又高效。

然后是Element Plus。市面上基于Vue 3的UI库不少,为什么我偏爱它?就三个字:全、稳、熟。“全”是指组件库覆盖了后台管理系统99%的场景。从基础的布局、按钮、表单,到复杂的表格、树形控件、步骤条,再到图表、富文本编辑器这类增强组件,它几乎都有现成的、经过大量项目验证的实现。你不需要为了一个日期范围选择器或者一个可拖拽排序的表格去满世界找第三方库,省去了大量集成和兼容性调试的时间。“稳”是指它的设计和代码质量。Element Plus脱胎于经典的Element UI,继承了其严谨的设计规范和稳定的API。在企业级项目里,稳定性和一致性往往比炫酷的动画更重要。它的组件设计符合大多数后台操作习惯,文档清晰,社区遇到问题也基本都能找到解决方案。“熟”则是对国内开发者非常友好。中文文档齐全,社区活跃,很多你想到的、没想到的“坑”,前辈们都踩过并分享了经验。这对于需要快速上手的团队项目来说,沟通和学习成本都低得多。

把这两者结合起来,你的开发体验会是这样:用Vite瞬间创建一个Vue 3项目,感受飞快的启动和热更新;然后引入Element Plus,像搭积木一样,用现成的高质量组件快速拼出页面的骨架;最后,用Vue 3的组合式API去填充那些独特的、复杂的业务逻辑。整个过程流畅自然,你可以把绝大部分精力聚焦在业务本身,而不是在框架选型、UI适配或者性能调优上反复折腾。接下来,我们就从零开始,一步步把这个“积木”搭起来。

2. 项目初始化与环境搭建:从零到一的正确姿势

万事开头难,但一个好的开始能让后续开发顺风顺水。这里我不只告诉你命令怎么敲,更想分享我实践中总结的、能让项目结构更清晰、更易于维护的初始化套路。

2.1 用Vite创建项目,为什么是现在的最佳选择?

几年前,Vue CLI是标准答案。但现在,我强烈推荐你从Vite开始。它的速度优势太明显了。用npm create vite@latest,感觉还没按回车,项目就建好了。冷启动和热更新快到让你怀疑人生,这在开发大型后台系统时,能显著提升你的“心流”体验,减少等待编译的烦躁感。我们来实际操作一下:

# 使用npm 7+,这个命令会启动一个交互式创建向导,非常方便
npm create vite@latest my-admin-system -- --template vue

# 进入项目目录
cd my-admin-system

# 安装依赖
npm install

执行完后,你会得到一个非常干净的项目结构。我建议你立刻做一件事:打开package.json,把scripts里的dev命令稍微改一下,加上--host。这样你可以在同一局域网下的其他设备(比如手机或测试机)上访问开发服务器,方便调试。

{
  "scripts": {
    "dev": "vite --host", // 加上 --host 参数
    "build": "vite build",
    "preview": "vite preview"
  }
}

然后运行npm run dev,你会看到一个本地地址和一个网络地址,这就搞定了。

2.2 引入Element Plus:全局引入还是按需引入?

这是很多人的第一个纠结点。我的建议是:在项目初期,尤其是学习或中小型项目,直接用全局引入,简单粗暴。等到项目体积变大,你明显感觉到首屏加载需要优化时,再考虑按需引入。过早优化是万恶之源。全局引入只需要两步:

首先,安装Element Plus和它的图标库(图标库是独立包,但很多组件会用到):

npm install element-plus @element-plus/icons-vue

然后,在main.js(或main.ts)中引入:

import { createApp } from 'vue'
import App from './App.vue'
// 1. 引入Element Plus核心库和样式
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
// 2. 引入图标库(可选,但推荐)
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const app = createApp(App)

// 3. 全局注册所有图标(方便在模板中直接使用 `<el-icon><Edit /></el-icon>`)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

// 4. 使用Element Plus
app.use(ElementPlus)
app.mount('#app')

搞定!现在你就可以在项目的任何地方使用<el-button>这样的组件了。这种方式的好处是心智负担小,不用担心哪个组件没引入。当然,它的代价是打包体积会包含整个Element Plus。但对于企业内网后台或者初期项目,这点体积通常不是问题。

2.3 项目结构规划:为增长做好准备

默认的src目录结构太简单了。一个可维护的企业级项目,需要有清晰的结构。我通常会这样组织,你可以参考:

src/
├── api/           # 所有接口请求函数,按模块划分(user.js, order.js)
├── assets/        # 静态资源(图片、字体等)
├── components/    # 全局公共组件(如SearchBar, Pagination)
│   ├── common/    # 真正全局通用的(按钮、弹窗确认框)
│   └── business/  # 业务通用组件(如UserSelector)
├── composables/   # Vue 3组合式函数(usePagination, useFormValidate)
├── directives/    # 自定义指令(如权限判断指令 v-permission)
├── layout/        # 布局组件(Header, Sidebar, AppMain)
├── router/        # 路由配置和路由守卫
├── store/         # Pinia状态管理(modules模式)
├── styles/        # 全局样式、变量覆盖、混入
├── utils/         # 工具函数(日期格式化、金额处理、正则校验)
├── views/         # 页面级组件(对应路由)
│   ├── dashboard/ # 每个页面可以有自己的子组件
│   ├── user/
│   └── ...
└── App.vue
main.js

不要一开始就把所有文件夹都建好,但要有这个意识。随着功能增加,把代码放到该放的位置,而不是全部堆在views里。比如,当你发现好几个页面都需要分页逻辑时,就把分页的currentPage、pageSize、total和翻页方法抽到composables/usePagination.js里,然后在各个页面导入使用。这才是Vue 3组合式API的精髓。

3. 核心功能开发:打造后台的骨架与血肉

一个后台管理系统,抛开具体业务,其核心功能模块是相通的。我们把这几块硬骨头啃下来,项目就完成了一大半。

3.1 布局架构:使用Container容器组件

Element Plus提供了<el-container>系列组件来快速搭建后台经典布局。我习惯在src/layout目录下创建一个index.vue文件作为主布局。

<!-- src/layout/index.vue -->
<template>
  <el-container class="layout-container">
    <!-- 侧边栏 -->
    <el-aside :width="asideWidth" class="aside">
      <div class="logo">Admin Pro</div>
      <SidebarMenu /> <!-- 这是一个自定义的导航菜单组件 -->
    </el-aside>

    <el-container>
      <!-- 顶部导航栏 -->
      <el-header class="header">
        <div class="left">
          <el-icon @click="toggleCollapse"><Fold /></el-icon>
          <Breadcrumb /> <!-- 面包屑组件 -->
        </div>
        <div class="right">
          <UserAvatar /> <!-- 用户头像下拉菜单 -->
        </div>
      </el-header>

      <!-- 标签页(如果有多页签需求) -->
      <!-- <TagsView /> -->

      <!-- 主内容区 -->
      <el-main class="main">
        <router-view v-slot="{ Component }">
          <transition name="fade-transform" mode="out-in">
            <keep-alive :include="cachedViews"> <!-- 缓存页面 -->
              <component :is="Component" />
            </keep-alive>
          </transition>
        </router-view>
      </el-main>

      <!-- 页脚 -->
      <!-- <el-footer>Footer</el-footer> -->
    </el-container>
  </el-container>
</template>

<script setup>
import { ref, computed } from 'vue'
import { Fold } from '@element-plus/icons-vue'
import SidebarMenu from './components/SidebarMenu.vue'
import Breadcrumb from './components/Breadcrumb.vue'
import UserAvatar from './components/UserAvatar.vue'

const isCollapse = ref(false)
// 根据折叠状态动态计算侧边栏宽度
const asideWidth = computed(() => isCollapse.value ? '64px' : '200px')

const toggleCollapse = () => {
  isCollapse.value = !isCollapse.value
}
</script>

<style scoped lang="scss">
.layout-container {
  height: 100vh;
  .aside {
    background-color: #304156;
    transition: width 0.3s;
    .logo {
      height: 60px;
      line-height: 60px;
      text-align: center;
      color: #fff;
      font-size: 18px;
      font-weight: bold;
      overflow: hidden;
    }
  }
  .header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    border-bottom: 1px solid #e6e6e6;
    background: #fff;
    .left {
      display: flex;
      align-items: center;
      .el-icon {
        margin-right: 15px;
        cursor: pointer;
        font-size: 20px;
      }
    }
  }
  .main {
    background-color: #f0f2f5;
    padding: 20px;
  }
}
</style>

这个布局组件包含了侧边栏折叠、面包屑导航、用户信息等常见元素。在App.vue中,我们只需要引入这个布局,并用<router-view>来承载页面内容。路由配置则对应到views目录下的各个页面组件。

3.2 路由与权限管理:动态路由的实践

后台系统几乎都涉及权限。一种简单有效的方案是动态路由。思路是:用户登录后,根据其角色或权限标识,从后端获取有权限访问的菜单列表,然后前端根据这个列表动态添加路由。

首先,在router/index.js中,我们定义一些所有用户都能访问的静态路由,比如登录页、404页。

import { createRouter, createWebHistory } from 'vue-router'

// 静态路由(无需权限)
export const constantRoutes = [
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/login/index.vue'),
    hidden: true // 不在侧边栏显示
  },
  {
    path: '/404',
    name: '404',
    component: () => import('@/views/error-page/404.vue'),
    hidden: true
  },
  // 重定向
  {
    path: '/',
    redirect: '/dashboard'
  }
]

// 创建路由实例,先只挂载静态路由
const router = createRouter({
  history: createWebHistory(),
  routes: constantRoutes
})

// 动态路由(需要权限)
export const asyncRoutes = [
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: () => import('@/views/dashboard/index.vue'),
    meta: { title: '仪表盘', icon: 'Odometer' }
  },
  {
    path: '/user',
    name: 'User',
    component: () => import('@/views/user/index.vue'),
    meta: { title: '用户管理', icon: 'User' }
  },
  // ... 更多需要权限的路由
]

export default router

然后,在用户登录成功后,我们调用一个generateRoutes函数。这个函数会根据用户权限,从asyncRoutes中过滤出有权限的路由,然后通过router.addRoute()动态添加到路由实例中。同时,这个过滤后的路由列表也用于生成侧边栏菜单。这里的关键是router.addRoute()的用法,它允许我们在运行时添加路由。记得,添加动态路由前,最好先移除之前可能添加的旧路由,避免重复。

3.3 表格页面的深度实践:封装一个万能表格组件

后台管理,十有八九是在和表格打交道。Element Plus的<el-table>功能强大,但每个页面都写一遍分页、查询、加载状态会很冗余。我的做法是封装一个高阶的表格组件。

假设我们有一个components/business/ProTable.vue:

<template>
  <div class="pro-table">
    <!-- 查询表单区域(可插槽自定义) -->
    <div v-if="$slots.search" class="search-area">
      <slot name="search"></slot>
    </div>

    <!-- 操作按钮区域 -->
    <div class="operation-area" v-if="$slots.operation || showAdd">
      <slot name="operation">
        <el-button v-if="showAdd" type="primary" @click="$emit('add')">新增</el-button>
      </slot>
    </div>

    <!-- 表格主体 -->
    <el-table
      v-loading="loading"
      :data="tableData"
      :border="border"
      :stripe="stripe"
      @selection-change="handleSelectionChange"
      v-bind="$attrs" <!-- 透传其他el-table属性 -->
    >
      <!-- 选择列 -->
      <el-table-column v-if="showSelection" type="selection" width="55" />
      <!-- 序号列 -->
      <el-table-column v-if="showIndex" type="index" label="序号" width="80" align="center" />
      <!-- 动态渲染列 -->
      <template v-for="col in columns" :key="col.prop">
        <el-table-column v-bind="col">
          <!-- 支持自定义单元格内容 -->
          <template v-if="col.slot" #default="scope">
            <slot :name="col.slot" :row="scope.row"></slot>
          </template>
        </el-table-column>
      </template>
      <!-- 操作列(可插槽自定义) -->
      <el-table-column v-if="showOperation" label="操作" width="180" fixed="right">
        <template #default="scope">
          <slot name="operation-column" :row="scope.row">
            <el-button link type="primary" size="small" @click="$emit('edit', scope.row)">编辑</el-button>
            <el-button link type="danger" size="small" @click="$emit('delete', scope.row)">删除</el-button>
          </slot>
        </template>
      </el-table-column>
    </el-table>

    <!-- 分页 -->
    <div class="pagination-area" v-if="showPagination">
      <el-pagination
        v-model:current-page="currentPage"
        v-model:page-size="pageSize"
        :page-sizes="[10, 20, 50, 100]"
        :total="total"
        layout="total, sizes, prev, pager, next, jumper"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>
  </div>
</template>

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

const props = defineProps({
  // 数据相关
  data: { type: Array, default: () => [] },
  loading: { type: Boolean, default: false },
  total: { type: Number, default: 0 },
  // 列配置
  columns: { type: Array, required: true },
  // 功能开关
  showSelection: { type: Boolean, default: false },
  showIndex: { type: Boolean, default: false },
  showOperation: { type: Boolean, default: true },
  showPagination: { type: Boolean, default: true },
  showAdd: { type: Boolean, default: true },
  // 样式
  border: { type: Boolean, default: true },
  stripe: { type: Boolean, default: true },
  // 分页初始值
  page: { type: Number, default: 1 },
  size: { type: Number, default: 10 }
})

const emit = defineEmits(['update:page', 'update:size', 'selection-change', 'add', 'edit', 'delete', 'query'])

const tableData = ref(props.data)
const currentPage = ref(props.page)
const pageSize = ref(props.size)
const selectedRows = ref([])

watch(() => props.data, (val) => { tableData.value = val })
watch(() => props.page, (val) => { currentPage.value = val })
watch(() => props.size, (val) => { pageSize.value = val })

const handleSelectionChange = (val) => {
  selectedRows.value = val
  emit('selection-change', val)
}

const handleSizeChange = (val) => {
  pageSize.value = val
  emit('update:size', val)
  emit('query')
}

const handleCurrentChange = (val) => {
  currentPage.value = val
  emit('update:page', val)
  emit('query')
}
</script>

在用户管理页面views/user/index.vue中,使用这个组件就变得非常简洁:

<template>
  <ProTable
    :columns="columns"
    :data="userList"
    :loading="loading"
    :total="total"
    v-model:page="queryParams.page"
    v-model:size="queryParams.size"
    @query="fetchUserList"
    @add="handleAdd"
    @edit="handleEdit"
  >
    <!-- 自定义查询表单插槽 -->
    <template #search>
      <el-form :inline="true" :model="queryParams">
        <el-form-item label="用户名">
          <el-input v-model="queryParams.name" placeholder="请输入" clearable />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="fetchUserList">查询</el-button>
        </el-form-item>
      </el-form>
    </template>

    <!-- 自定义操作列插槽 -->
    <template #operation-column="{ row }">
      <el-button link type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
      <el-button link type="danger" size="small" @click="handleDelete(row)">删除</el-button>
      <el-button link type="warning" size="small" @click="handleResetPwd(row)">重置密码</el-button>
    </template>
  </ProTable>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import ProTable from '@/components/business/ProTable.vue'
import { getUserList } from '@/api/user'

const columns = ref([
  { prop: 'username', label: '用户名', width: 120 },
  { prop: 'nickname', label: '昵称' },
  { prop: 'phone', label: '手机号' },
  { prop: 'status', label: '状态', slot: 'status' }, // 使用插槽自定义状态显示
  { prop: 'createTime', label: '创建时间', width: 180 }
])

const userList = ref([])
const loading = ref(false)
const total = ref(0)
const queryParams = ref({
  name: '',
  page: 1,
  size: 10
})

const fetchUserList = async () => {
  loading.value = true
  try {
    const res = await getUserList(queryParams.value)
    userList.value = res.data.list
    total.value = res.data.total
  } finally {
    loading.value = false
  }
}

onMounted(() => {
  fetchUserList()
})
</script>

通过这样的封装,表格的通用逻辑被收拢,页面只需关注业务数据和交互。这才是高效开发的样子。

4. 样式定制与主题切换:让系统拥有品牌感

一个专业的企业后台,UI应该符合公司的品牌形象,而不是千篇一律的默认蓝色。Element Plus提供了非常灵活的定制方案。

4.1 全局主题定制:修改SCSS变量

这是最彻底的定制方式。我们在项目根目录创建styles/element/index.scss文件:

// styles/element/index.scss

// 只需要重写你需要的变量
// 主色调
$--color-primary: #1890ff; // 品牌蓝
$--color-success: #52c41a;
$--color-warning: #faad14;
$--color-danger: #f5222d;
$--color-info: #909399;

// 背景色
$--background-color-base: #f5f7fa;

// 边框
$--border-color-base: #dcdfe6;
$--border-radius-base: 4px;

// 字体
$--font-path: 'element-plus/theme-chalk/fonts'; // 确保字体路径正确

// 引入Element Plus的SCSS入口文件
@use "element-plus/theme-chalk/src/index.scss" as *;

然后,在main.js中引入这个文件,替换掉之前引入的CSS文件:

// main.js
// 删除这行:import 'element-plus/dist/index.css'
// 添加这行:
import '@/styles/element/index.scss'

这样,所有基于这些变量的组件样式都会自动更新。你可以去Element Plus的官方文档找到完整的SCSS变量列表,按需修改。

4.2 暗黑模式一键切换

现在很多系统都支持暗黑模式。Element Plus内置了暗黑主题,我们可以结合一个状态管理(比如Pinia)和CSS变量,实现一键切换。

首先,安装并配置Pinia(Vue官方推荐的状态管理库):

npm install pinia
// store/index.js
import { createPinia } from 'pinia'
export const pinia = createPinia()

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import { pinia } from './store'
app.use(pinia)

创建一个管理主题的store:

// store/modules/theme.js
import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useThemeStore = defineStore('theme', () => {
  const theme = ref('light') // 'light' or 'dark'

  const toggleTheme = () => {
    theme.value = theme.value === 'light' ? 'dark' : 'light'
    // 切换时,给html标签加上/移除 'dark' 类名
    if (theme.value === 'dark') {
      document.documentElement.classList.add('dark')
    } else {
      document.documentElement.classList.remove('dark')
    }
    // 你也可以把主题偏好保存到localStorage
    localStorage.setItem('theme', theme.value)
  }

  const initTheme = () => {
    const savedTheme = localStorage.getItem('theme') || 'light'
    theme.value = savedTheme
    if (savedTheme === 'dark') {
      document.documentElement.classList.add('dark')
    }
  }

  return { theme, toggleTheme, initTheme }
})

在App.vue的onMounted中初始化主题。然后,在全局样式文件(如styles/index.scss)中,利用Element Plus的暗黑模式类名来覆盖样式:

// styles/index.scss
// 暗黑模式下的自定义覆盖
html.dark {
  // 背景色
  --el-bg-color-page: #0a0a0a;
  --el-bg-color: #141414;
  --el-bg-color-overlay: #1d1e1f;

  // 文字色
  --el-text-color-primary: #e5eaf3;
  --el-text-color-regular: #cfd3dc;

  // 边框色
  --el-border-color: #434343;
  --el-border-color-light: #434343;
}

最后,在布局的右上角放一个切换按钮,调用themeStore.toggleTheme()即可。Element Plus的组件会自动响应html.dark这个类名,应用其内置的暗黑样式,而我们自定义的CSS变量会进一步微调全局背景和文字颜色,达到和谐统一的效果。

4.3 组件级样式覆盖:深度选择器的正确用法

有时候我们只想修改某个特定场景下组件的样式。比如想让某个页面里的所有按钮圆角大一些。可以使用深度选择器:deep()。

<template>
  <div class="special-page">
    <el-button type="primary">特制按钮</el-button>
  </div>
</template>

<style scoped lang="scss">
.special-page {
  // 使用 :deep() 穿透scoped限制,修改子组件样式
  :deep(.el-button) {
    border-radius: 20px;
  }
}
</style>

注意:在Vue 3的单文件组件中,使用<style scoped>时,如果要修改子组件(如Element Plus组件)的样式,必须使用:deep()选择器。直接写.el-button是无效的。这是Vue 3为了更好的样式隔离引入的规则。

5. 状态管理与数据请求:让数据流动清晰可控

后台系统数据流复杂,好的状态管理能让代码像乐高一样清晰可组合。我现在的首选是 Pinia + 基于Composition API封装的数据请求。

5.1 使用Pinia管理全局状态

Pinia比Vuex更简单,完全支持Composition API,并且TypeScript友好。我们用它来管理用户信息、权限、主题等全局状态。

创建用户store:store/modules/user.js

import { defineStore } from 'pinia'
import { ref } from 'vue'
import { login, logout, getUserInfo } from '@/api/user'

export const useUserStore = defineStore('user', () => {
  const token = ref(localStorage.getItem('token') || '')
  const userInfo = ref({})
  const roles = ref([])

  const setToken = (newToken) => {
    token.value = newToken
    localStorage.setItem('token', newToken)
  }

  const loginAction = async (loginForm) => {
    try {
      const res = await login(loginForm)
      setToken(res.data.token)
      // 登录成功后获取用户信息
      await getUserInfoAction()
      return Promise.resolve(res)
    } catch (error) {
      return Promise.reject(error)
    }
  }

  const getUserInfoAction = async () => {
    if (!token.value) return
    const res = await getUserInfo()
    userInfo.value = res.data.user
    roles.value = res.data.roles
  }

  const logoutAction = async () => {
    try {
      await logout()
    } finally {
      // 无论后端是否成功,前端都清空状态
      token.value = ''
      userInfo.value = {}
      roles.value = []
      localStorage.removeItem('token')
      // 跳转到登录页
      router.push('/login')
    }
  }

  return {
    token,
    userInfo,
    roles,
    setToken,
    loginAction,
    getUserInfoAction,
    logoutAction
  }
})

在组件中使用时,直接导入并调用即可,非常直观:

<script setup>
import { useUserStore } from '@/store/modules/user'
const userStore = useUserStore()

const handleLogin = async () => {
  await userStore.loginAction({ username: 'admin', password: '123456' })
}
</script>

5.2 封装一个“聪明”的请求层

直接使用axios发请求,每个请求都要写try-catch,重复代码太多。我习惯封装一个更易用的请求层。首先是用axios创建实例,配置拦截器。

// utils/request.js
import axios from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'

const service = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_API,
  timeout: 10000
})

// 请求拦截器
service.interceptors.request.use(
  (config) => {
    const userStore = useUserStore() // 注意:需要在能访问到pinia的地方调用
    if (userStore.token) {
      config.headers['Authorization'] = `Bearer ${userStore.token}`
    }
    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  (response) => {
    const res = response.data
    // 假设后端统一返回格式: { code: 200, data: {}, message: 'success' }
    if (res.code === 200) {
      return res
    } else {
      // 业务错误
      ElMessage.error(res.message || 'Error')
      // 特殊错误码处理,如未登录(401)
      if (res.code === 401) {
        // 触发登出
        userStore.logoutAction()
        router.push('/login')
      }
      return Promise.reject(new Error(res.message || 'Error'))
    }
  },
  (error) => {
    // HTTP错误(网络错误、超时等)
    ElMessage.error(error.message || 'Request Error')
    return Promise.reject(error)
  }
)

export default service

更进一步,我们可以基于这个service,封装一个更符合Hooks风格的请求函数,在composables/目录下创建useRequest.js:

// composables/useRequest.js
import { ref } from 'vue'
import service from '@/utils/request'

export function useRequest(options) {
  const loading = ref(false)
  const data = ref(null)
  const error = ref(null)

  const run = async (params) => {
    loading.value = true
    error.value = null
    try {
      const result = await service({
        ...options,
        ...(params && { data: params }) // 或 params,根据请求方法调整
      })
      data.value = result.data
      return Promise.resolve(result)
    } catch (err) {
      error.value = err
      return Promise.reject(err)
    } finally {
      loading.value = false
    }
  }

  // 立即执行(可选)
  if (options.immediate) {
    run()
  }

  return {
    loading,
    data,
    error,
    run
  }
}

在组件中使用时,代码会非常简洁:

<script setup>
import { useRequest } from '@/composables/useRequest'

// 获取用户列表
const { loading: userLoading, data: userList, run: fetchUsers } = useRequest({
  url: '/api/users',
  method: 'get'
})

// 提交表单
const { loading: submitLoading, run: submitForm } = useRequest({
  url: '/api/user',
  method: 'post'
})

onMounted(() => {
  fetchUsers() // 手动触发
})

const handleSubmit = async (formData) => {
  await submitForm(formData)
  ElMessage.success('创建成功')
  fetchUsers() // 重新拉取列表
}
</script>

这种模式把加载状态、数据和错误都集中管理,逻辑清晰,复用性极高。

6. 打包优化与部署上线:让应用飞起来

开发完成,最后一步就是打包部署。这里有几个关键点能显著提升用户体验。

6.1 生产环境构建优化

直接运行npm run build没问题,但我们可以通过vite.config.js进行一些优化配置。

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src') // 路径别名
    }
  },
  build: {
    // 生产环境移除 console 和 debugger
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    },
    // 资源分块策略
    rollupOptions: {
      output: {
        chunkFileNames: 'static/js/[name]-[hash].js',
        entryFileNames: 'static/js/[name]-[hash].js',
        assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
        // 手动拆包,把 node_modules 里的大依赖单独打包
        manualChunks(id) {
          if (id.includes('node_modules')) {
            if (id.includes('element-plus')) {
              return 'element-plus'
            }
            if (id.includes('vue')) {
              return 'vue'
            }
            return 'vendor'
          }
        }
      }
    }
  },
  // 开发服务器配置
  server: {
    port: 3000,
    open: true,
    proxy: { // 配置代理,解决跨域
      '/api': {
        target: 'http://your-backend-api.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
})

这个配置做了几件事:1) 设置了路径别名,方便导入;2) 打包时移除调试代码;3) 对输出文件进行哈希命名,利于缓存;4) 将element-plus、vue等大依赖单独打包,利用浏览器并行加载和缓存;5) 配置了开发服务器的代理。

6.2 使用插件实现按需引入

如果项目后期体积确实成了问题,可以引入unplugin-vue-components和unplugin-auto-import这两个Vite插件,实现真正的按需自动引入。这会让你的main.js非常干净,组件也无需手动import。

npm install -D unplugin-vue-components unplugin-auto-import
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  plugins: [
    vue(),
    // 自动导入 Vue 相关函数,如:ref, reactive, computed 等
    AutoImport({
      imports: ['vue', 'vue-router'],
      resolvers: [ElementPlusResolver()],
      dts: 'src/auto-imports.d.ts' // 生成类型声明文件
    }),
    // 自动导入组件
    Components({
      resolvers: [
        ElementPlusResolver(),
      ],
      dts: 'src/components.d.ts' // 生成类型声明文件
    })
  ]
})

配置好后,你在模板中直接使用<el-button>,插件会自动帮你引入ElButton组件和对应的CSS。<script setup>里直接写ref、onMounted,插件也会自动导入。非常智能,能极大减少样板代码。

6.3 部署到常见服务器

打包后生成的dist目录是纯静态文件,可以部署到任何Web服务器。

Nginx配置示例:

server {
    listen       80;
    server_name  your-domain.com;

    # 静态资源
    location / {
        root   /path/to/your/dist;
        index  index.html;
        try_files $uri $uri/ /index.html; # 支持Vue Router的history模式
    }

    # 代理API请求到后端
    location /api/ {
        proxy_pass http://your-backend-server:port/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # 开启gzip压缩
    gzip on;
    gzip_min_length 1k;
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript;
}

Docker部署(更现代的方式): 创建一个简单的Dockerfile:

# 使用Node镜像构建
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# 使用Nginx镜像提供服务
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

然后构建并运行镜像:

docker build -t my-admin .
docker run -d -p 8080:80 --name admin-container my-admin

访问http://localhost:8080就能看到你的应用了。Docker化部署保证了环境一致性,迁移和扩展都更方便。

走到这一步,一个功能完整、结构清晰、性能优良且易于部署的企业级后台管理系统就真正从你的手中诞生了。从项目初始化到核心功能开发,再到样式定制和最终部署,每一个环节的选择和实践都凝聚了实际项目中的经验与思考。记住,技术栈是工具,最终目的是高效、稳定地实现业务价值。Vue 3和Element Plus这个组合,能让你在追求这个目标的路上,走得更稳、更快。剩下的,就是在具体的业务场景中,不断打磨和迭代你的系统了。如果在实践中遇到具体问题,不妨多看看官方文档和社区讨论,很多坑都已经有现成的解决方案了。

Vue 3 + Element Plus 企业级后台管理系统实战全解析 本文全面解析了如何使用 Vue 3 和 Element Plus 构建企业级后台管理系统。从项目初始化、工程化配置,到核心架构设计(路由、状态、权限一体化),再到复杂业务页面(如用户管理)的实战封装,提供了完整的开发指南和性能优化建议,帮助开发者高效搭建稳定、可维护的后台系统。 阅读详情

相关推荐

使用vue3+element-ui plus 快速构建后台管理模板

使用vue3+element-ui plus 快速构建后台管理模板,分为侧边栏,顶部栏和内容区域三个块,前两个抽离为组件,内容区域使用routerview跳转即可,组件通信使用父子组件的通信方式。element ui plus 主要使用了 container布局容器以及icon图标。

m0_63622279的博客 1万+

基于VUE3和ElementPlus从头搭建通后台管理系统(前端篇)

从零开始搭建完整的通用管理后台前端,使用vue3+elementplus搭建系统UI界面,使用mock模拟数据,使用echarts实现系统可视化模块,可以此项目为基础进行扩展开发,快速搭建管理系统,具体内容如下:常见功能实现:实现用户登录(用户名密码登录、手机验证码登录、二维码扫码)、找回密码(邮箱找回、短信验证码找回)、系统主页、用户管理、系统配置、数据字典、日志管理、菜单管理、角色管理、消息管理、验证码管理、可视化界面等管理系统的常见功能。公共常用组件:实现公共组件的抽离以便能进行扩展及复用,包括通用表格、通用编辑器、通用详情页、关联对象多选框、关联对象单选框、关联对象树形选择框、多级联动选择框、自动搜索保存组件、列表选择框组件等常用的组件。源码获取方式:点击 https://gitcode.net/invite_link?invite_code=asqckJc7HUKjU_HuLjX4 申请加入项目,申请后等待审核,审核通过后即可使用git检出项目,请购买课程后再申请,若为学习会员请留言说明,留言时请备注gitcode中的昵称信息,谢谢。项目效果预览:

DOA估计算法从原理到仿真——CBF、Capon、MUSIC算法

从原理出发理解并实现经典的DOA估计算法

m0_55845774的博客 1万+

基于Vue3 + Element Plus 的后台管理系统详细教程

以上是使用 Vue3 和 Element Plus 创建后台管理系统的基本步骤,但如果你想要实现一个实用的后台管理系统,还需要实现登录、权限管理、数据增删改查等功能。Vue 3 中最大的新特性是引入了 Composition API,它允许开发人员按照逻辑相关的方式组织代码,可以更好地复用组件和处理复杂的逻辑。在 data 中声明登录所需参数,初始化表单验证规则并在 method 中实现表单的提交、参数的验证与登录状态的切换。使用 Element Plus 提供的表格、表单等组件实现数据的增删改查。

一步一个脚印,踏实努力,向着目标前进 2万+

vue微前端qiankun框架学习到项目实战,基座登录动态菜单及权限控制

技术无关,独立开发,独立部署,增量升级,独立运行,微前端的核心目标是将巨石应用拆解成若干可以自治的松耦合微应用。

前端开发工程师 9752

数据驱动的老年人健康管理与生活品质提升实践研究

摘要 全球人口老龄化趋势显著,2025年65岁以上人口预计达8.57亿,中国老年人口已突破3亿。老年人健康挑战突出,75%以上患慢性病,且独居老人心理健康与应急需求迫切。数据驱动的健康管理通过智能设备(如跌倒检测、远程监测)和AI技术(如疾病预测算法)提升效率,改善生活品质。研究提出差异化需求解决方案,如慢性病患者的持续监测与用药管理,高龄老人的综合护理服务,并探讨政策与商业模式以推动智慧养老产业发展,2024年市场规模已达6.8万亿元。

聚焦程序设计语言理论与前沿技术落地,拆解代码世界的底层逻辑,探索语言设计的 “创造与沟通” 之道。从编译原理、类型系统到 AI 技术在金融、医疗、制造、养老、大健康等领域的实战案例,分享硬核技术干货与工程化思考。 1632

Vue3+element-plus实现后台管理系统

c. npm config set registry=https://registry.npmmirror.com 切换成国内镜像。d. 初始化工程 npm init vue@latest shop-admin -- --template vue。1、安装的插件 vscode中文显示插件 2、高亮提示插件volar 3、vue 3 sni 代码提示。安装: npm install element-plus --save。中文地址:https://cn.windicss.org/

shi450561200的博客 5232

Python验证码识别神器ddddocr:5分钟搞定网站登录自动化(附完整代码)

本文详细介绍了如何使用Python开源库ddddocr快速实现验证码识别,结合Selenium完成网站登录自动化。ddddocr作为轻量级OCR工具,支持多种验证码类型,识别速度快且准确率高,特别适合自动化测试和爬虫开发。文章包含完整代码示例,从环境配置到实战应用,帮助开发者5分钟内实现验证码破解。

sprite的博客 798

1小时搞定Vue3+vite+Element UI或者Element Plus开发后台管理系统,使用vite构建管理项目,前后端分离包含mysql数据库,网站后台

我们通常给小程序或者app开发后台时,不可避免的要用到可视化的数据管理后台,而vue和Element是我们目前比较主流的开发管理后台的主流搭配。所以今天石头哥就带大家来一起学习下vue3和Element plus的开发。

编程小石头 7712

QT+Opencv实现视频流播放

1.流程 首先通过opencv打开视频流,获取到数据帧,然后将数据帧转换为QT可识别的图像,显示到QT界面上。 2.opencv解码线程源码 //DecodeOpencv.h #pragma once #include <QThread> #include <QObject> #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/img..

希望能帮助大家,希望大家多多支持,你们的支持是我前进的动力。 6460

【DaVinci Developer专题】-23-IDT高阶应用:结构体类型的数组

共8页 目录 1 创建一个Record IDT 2 创建一个Array IDT 3 创建一个S/R Port Interface 4 创建一个Constant 5 导入CFG生成代码的总结 6 结尾 <--返回「实战篇」博文-总目录--> <--返回「Autosar从入门到精通-实战篇」专栏主页--> 关键字: 缩略语/术语 含义 DEV DaVinci Developer .

「汽车电子助手」的博客 1670

vue3-admin后台管理系统: 使用Vue3+Vue-Router4 + Element-Plus打造高效后台管理系统

摘要: 本文将深入探讨如何使用Vue3、Vite2、Vue-Router4和Element-Plus等前端技术栈,构建一套高效、稳定的后台管理系统。我们将结合newbee-mall项目的实践经验,介绍系统的技术实现细节,包括前后端分离架构的优势、关键技术栈的选择、模块划分和功能实现等。通过本文,读者可以更好地了解如何利用现代前端框架和库构建高效的后台管理系统,并应用于实际开发中。随着企业信息化程度的提高,后台管理系统已成为企业运营的关键支撑工具。为了满足企业对后台管理系统的需求,我们采用了Vue3、Vite

chenchuang0128的博客 1万+

MATLAB算法实战应用案例精讲-【数模应用】梯度提升决策树(GDBT)(附python代码)

GBDT(Gradient Boosting Decision Tree) 又叫 MART(Multiple Additive Regression Tree),是一种迭代的决策树算法,该算法由多棵决策树组成,它在被提出之初就和SVM一起被认为是泛化能力(generalization)较强的算法。近些年更因为被用于搜索排序的机器学习模型而引起大家关注。.........

qq_36130719的博客 2079

实战Vue-Element-Plus-Admin:从零构建企业级管理后台的完整指南

Vue-Element-Plus-Admin是基于Vue3、TypeScript、Element Plus和Vite构建的现代化后台管理系统,专为企业级应用提供完整的解决方案。无论你是前端新手还是资深开发者,这个框架都能帮助你快速搭建功能完善的管理界面。🎯 ## 🚀 新手入门:三步骤快速上手 对于初次接触这个框架的开发者,建议按照以下流程开始: **第一步:环境准备与项目获取** ```

gitblog_00061的博客 1057

面经-2023-禾赛-FPGA工程师

面经-2023-禾赛-FPGA工程师

w0shishabi的博客 2970

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Element Plus Admin:Vue 3企业级后台管理系统完全实战手册

还在为搭建企业级后台管理系统而烦恼吗?Element Plus Admin正是你需要的解决方案!这个基于Vue 3 + TypeScript + Vite的现代化管理平台,让你在10分钟内就能部署一个功能完备的后台系统。无论你是前端新手还是资深开发者,都能快速上手,开启高效开发之旅! ## 🎯 5分钟快速启动:从零到部署 ### 环境准备与项目获取 首先,确保你的电脑已经安装了Node.j

gitblog_00658的博客 697

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Vue3+Element Plus终极管理框架:打造企业级中台系统的完整指南

element-plus-admin是基于Vue3、TypeScript和Vite构建的现代化后台管理系统模板,专为中大型企业级应用设计。该项目集成了完整的权限控制体系、多主题配置方案和高性能构建工具链,为企业开发团队提供开箱即用的中台解决方案。 ## 技术架构深度解析 element-plus-admin采用前沿技术栈组合,为复杂业务场景提供稳定基础: | 技术组件 | 版本 | 核心价值

gitblog_00459的博客 918

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Vue3 企业级后台管理系统:从0到1的搭建之旅

Vue3 企业级后台管理系统:从0到1的搭建之旅

fruge365的博客 1203

Vue-Element-Plus-Admin企业级后台系统全方位实战指南

想要快速构建专业的企业级后台管理系统吗?Vue-Element-Plus-Admin为你提供了完整的解决方案。这款基于Vue3、TypeScript、Element Plus和Vite技术栈的现代化后台框架,集成了权限管理、动态路由、多标签页等核心功能,让开发工作变得轻松高效。🚀 ## 🎯 快速上手:5分钟搭建开发环境 ### 环境配置与项目启动 首先确保你的开发环境满足基础要求:Nod

gitblog_00288的博客 496

Vue-Element-Plus-Admin:企业级后台管理系统的完整解决方案

Vue-Element-Plus-Admin是基于Vue3、TypeScript、Element Plus和Vite构建的现代化后台管理系统框架,专为企业级应用设计。这套开箱即用的后台集成方案提供了完整的权限管理、动态路由、多标签页等核心功能,帮助开发者快速搭建稳定高效的后台管理界面。 ## 🎯 项目核心价值与定位 **功能定位**:作为后台集成方案而非基础模板,Vue-Element-Pl

gitblog_00826的博客 583

Vue-Element-Plus-Admin:打造现代化企业后台管理系统的终极指南

Vue-Element-Plus-Admin 是一个基于 Vue3、TypeScript、Element-Plus 和 Vite4 构建的现代化后台管理系统解决方案,专为企业级应用开发而设计。这套开箱即用的中后台前端集成方案,集成了丰富的功能模块和最佳实践,帮助开发者快速构建专业的企业管理平台。 ## 🚀 项目核心优势与特性 ### 前沿技术栈支持 Vue-Element-Plus-Admi

gitblog_00317的博客 903

Vue-Element-Plus-Admin完全掌握:企业级后台管理系统的终极指南

你是否曾经为搭建后台管理系统而头疼不已?面对复杂的权限管理、动态路由配置、多标签页切换这些需求,是不是感觉无从下手?别担心,Vue-Element-Plus-Admin就是为你量身打造的解决方案! ## 痛点直击:后台系统开发三大难题 **难题一:权限管理复杂难控** 传统的权限管理往往需要手动配置每个用户的菜单权限,这不仅耗时耗力,还容易出错。Vue-Element-Plus-Admin通过

gitblog_00883的博客 528

RuoYi-Vue3终极指南:5步打造企业级后台管理系统

还在为搭建企业级后台管理系统而烦恼吗?RuoYi-Vue3作为一款基于Java Spring Boot和Vue3技术栈的现代化管理平台,为你提供了一整套快速开发解决方案。无论你是技术新手还是资深开发者,这份完整指南都将帮助你快速掌握这个强大的工具。 ## 🚀 为什么选择RuoYi-Vue3? RuoYi-Vue3采用了当前最前沿的前端技术栈,包括Vue 3.5.16、Element Plus

gitblog_00962的博客 646

Vue3+Element Plus后台系统开发实战:从零到精通的完整指南

还在为后台管理系统的重复搭建而头疼吗?今天我要分享一个基于Vue3和Element Plus的高效开发方案,让你彻底告别重复造轮子的烦恼!🚀 ## 为什么选择这个技术组合? 作为一名前端开发者,我深知后台系统开发的痛点:每次都要重新配置路由、权限验证、菜单系统……这些基础工作占据了大量时间,却很少带来成就感。 **Vue3与Element Plus的黄金搭档优势:** - 🎯 组合式AP

gitblog_00594的博客 291

vue-pure-admin:开源中后台管理系统模板,Vue3+Element-Plus打造开箱即用解决方案

你是否还在为中后台系统搭建重复编写基础架构?是否因技术选型混乱导致后期维护困难?是否面临UI组件库与业务逻辑整合的繁琐工作?vue-pure-admin作为一款开源免费的中后台管理系统模板,基于Vue3、Element-Plus和TypeScript技术栈,提供了开箱即用的解决方案,帮助开发者快速构建企业级管理系统。 读完本文后,你将获得: - 掌握vue-pure-admin的核心架构与技术亮...

gitblog_00807的博客 1381

Vue3-admin:基于Vue3和Element Plus的终极后台管理系统解决方案

Vue3-admin是一个基于Vue3和Element Plus的开源后台管理模板项目,它预设了丰富的UI组件和管理功能模块,能够显著简化企业级管理系统的开发流程。该项目采用最新的Vue3技术和Element Plus组件库,非常适合Vue.js开发者、前端工程师以及需要快速构建企业级管理系统的全栈开发者使用。 ## 🌟 项目核心优势 Vue3-admin作为一款现代化的后台管理系统解决方案

gitblog_00618的博客 437
上一篇: Electron应用打包神器:NSIS从安装到实战配置全流程(Windows版)
下一篇: Weblogic反序列化漏洞的幕后故事:CVE-2019-2725漏洞原理深入解析
sql99
博客等级 码龄19年 168粉丝 1905原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值