Vue3 实战:从 0 搭建企业级后台管理系统(Router+Pinia+Axios+Element Plus 全整合)

news2026/3/17 13:54:33
前言后台管理系统是前端开发中最常见的业务场景之一也是 Vue 生态工具整合应用的典型案例。很多新手在学习 Vue3 时往往只会单独使用某个工具比如只写路由、只做状态管理但到了实际项目中如何把 Vue Router、Pinia、Axios、Element Plus 这些核心工具串联起来实现一个完整的后台系统却常常无从下手。本文将从实战落地角度手把手教你从 0 搭建一个具备完整功能的后台管理系统核心模块涵盖多页面路由配置与导航、Pinia 用户状态持久化、Axios 企业级请求封装、Element Plus 登录页与首页开发。内容覆盖基础配置、核心逻辑、踩坑解决方案既适合 Vue3 新手入门实战也能为中后台项目开发提供可落地的参考方案。一、项目初始化与技术栈选型1.1 核心技术栈说明本次实战项目采用 Vue3 生态最主流、最稳定的技术组合适配企业级开发标准技术工具核心作用选择理由Vue3 Vite基础框架 / 构建工具Vue3 组合式 API 开发效率高Vite 打包速度比 Webpack 快 3-5 倍Vue Router 4路由管理Vue3 官方配套路由支持组合式 API、路由守卫Pinia 2状态管理替代 Vuex更轻量、支持 TS、无需嵌套模块Axios网络请求支持拦截器、取消请求、跨域处理后台系统请求必备Element PlusUI 组件库企业级后台组件丰富适配 Vue3文档友好TypeScript类型校验提升代码可维护性减少生产环境 bug1.2 环境准备确保本地已安装以下环境Node.js ≥ 14.18.0推荐 16/18npm/pnpm/yarn推荐 pnpm速度更快、体积更小VS Code安装 Vetur/Vue Official 扩展1.3 项目创建与依赖安装步骤 1创建 Vue3TSVite 项目# 创建项目命名为vue-admin-demo pnpm create vite vue-admin-demo --template vue-ts # 进入项目目录 cd vue-admin-demo # 安装基础依赖 pnpm install步骤 2安装核心依赖# 路由 pnpm add vue-router4 # 状态管理 pnpm add pinia pinia-plugin-persistedstate # 网络请求 pnpm add axios # UI组件库 pnpm add element-plus # Element Plus按需引入插件 pnpm add unplugin-vue-components unplugin-auto-import -D1.4 项目目录结构梳理初始化后整理项目结构符合后台系统开发规范vue-admin-demo/ ├── src/ │ ├── api/ # Axios封装与接口请求 │ ├── assets/ # 静态资源图片、样式 │ ├── components/ # 通用组件 │ ├── layouts/ # 布局组件后台首页布局 │ ├── pinia/ # Pinia状态管理 │ ├── router/ # 路由配置 │ ├── styles/ # 全局样式 │ ├── utils/ # 工具函数 │ ├── views/ # 页面视图登录、首页、列表、详情 │ ├── App.vue # 根组件 │ ├── main.ts # 入口文件 │ └── vite-env.d.ts # TS类型声明 ├── .env.development # 开发环境变量 ├── vite.config.ts # Vite配置 └── package.json # 依赖配置二、Vue Router 多页面应用开发2.1 路由核心逻辑梳理后台系统的路由核心需求区分无需登录可访问登录页和需登录访问首页、列表页、详情页支持页面间导航导航菜单、编程式跳转详情页支持参数传递如商品 ID、用户 ID全局路由守卫校验登录状态用 Mermaid 可视化路由结构2.2 路由配置实现步骤 1创建路由配置文件src/router/index.tsimport { createRouter, createWebHistory, RouteRecordRaw } from vue-router; import { useUserStore } from /pinia/user; // 后续Pinia会实现 // 定义路由规则 const routes: ArrayRouteRecordRaw [ { path: /, redirect: /home, // 默认跳转到首页 }, { path: /login, name: Login, component: () import(/views/Login/index.vue), // 懒加载 meta: { requiresAuth: false, // 无需登录 title: 登录页, }, }, { path: /home, name: Home, component: () import(/views/Home/index.vue), meta: { requiresAuth: true, // 需要登录 title: 后台首页, }, }, { path: /list, name: List, component: () import(/views/List/index.vue), meta: { requiresAuth: true, title: 数据列表页, }, }, { path: /detail/:id, // 动态参数id name: Detail, component: () import(/views/Detail/index.vue), meta: { requiresAuth: true, title: 详情页, }, }, // 404页面 { path: /:pathMatch(.*)*, redirect: /home, }, ]; // 创建路由实例 const router createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes, }); // 全局路由守卫校验登录状态 router.beforeEach((to, from, next) { // 设置页面标题 document.title to.meta.title as string || Vue后台管理系统; const userStore useUserStore(); // 判断当前路由是否需要登录 if (to.meta.requiresAuth) { if (userStore.token) { // 已登录正常访问 next(); } else { // 未登录跳转到登录页 next({ path: /login }); } } else { // 无需登录直接访问 next(); } }); export default router;步骤 2在入口文件注册路由src/main.tsimport { createApp } from vue; import App from ./App.vue; // 引入路由 import router from ./router; // 引入Pinia后续配置 import { createPinia } from pinia; import piniaPluginPersistedstate from pinia-plugin-persistedstate; // 引入Element Plus后续配置 import ElementPlus from element-plus; import element-plus/dist/index.css; // 创建Pinia实例 const pinia createPinia(); pinia.use(piniaPluginPersistedstate); // 持久化插件 const app createApp(App); // 注册插件 app.use(router); app.use(pinia); app.use(ElementPlus); app.mount(#app);步骤 3修改根组件src/App.vuetemplate !-- 路由出口所有页面都会渲染到这里 -- router-view / /template script setup langts // 根组件仅作为路由容器 /script style scoped * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: Microsoft YaHei, sans-serif; } /style2.3 路由导航与参数传递1. 声明式导航导航菜单 / 按钮在首页Home添加导航按钮跳转到列表页 / 详情页template div classhome h2后台首页/h2 !-- 跳转到列表页 -- el-button typeprimary click$router.push(/list) 前往数据列表页 /el-button !-- 跳转到详情页传递参数id1 -- el-button typesuccess click$router.push(/detail/1) 查看ID1的详情 /el-button !-- 编程式导航的另一种写法 -- el-button typewarning clicktoDetail(2) 查看ID2的详情 /el-button /div /template script setup langts import { useRouter } from vue-router; const router useRouter(); // 编程式导航传递参数 const toDetail (id: number) { router.push({ path: /detail/${id}, // 也可以用query传递参数 // query: { id: id } }); }; /script2. 详情页接收参数src/views/Detail/index.vuetemplate div classdetail h2详情页/h2 p当前详情ID{{ id }}/p !-- 如果用query传递取值$route.query.id -- el-button click$router.go(-1)返回上一页/el-button /div /template script setup langts import { useRoute } from vue-router; const route useRoute(); // 获取params参数注意需添加TS类型断言 const id route.params.id as string; /script三、Pinia 用户状态管理登录状态 持久化3.1 Pinia 核心优势对比 Vuex3.2 定义用户 Storesrc/pinia/user.tsimport { defineStore } from pinia; // 定义用户信息类型 interface UserInfo { id: number; username: string; avatar: string; roles: string[]; } // 定义Store状态类型 interface UserState { token: string; // 登录令牌 userInfo: UserInfo | null; // 用户信息 isLogin: boolean; // 是否登录 } // 定义Store export const useUserStore defineStore(user, { // 状态初始化 state: (): UserState ({ token: , userInfo: null, isLogin: false, }), // 计算属性类似Vuex的getters getters: { // 获取用户角色简化写法 userRoles: (state) state.userInfo?.roles || [], }, // 方法同步/异步类似Vuex的mutationsactions actions: { // 登录存储token和用户信息 login(token: string, userInfo: UserInfo) { this.token token; this.userInfo userInfo; this.isLogin true; }, // 退出登录清空状态 logout() { this.token ; this.userInfo null; this.isLogin false; // 退出后跳转到登录页 window.location.href /login; }, // 更新用户信息 updateUserInfo(userInfo: PartialUserInfo) { if (this.userInfo) { this.userInfo { ...this.userInfo, ...userInfo }; } }, }, // 持久化配置关键 persist: { enabled: true, // 开启持久化 strategies: [ { key: vue_admin_user, // 本地存储的key storage: localStorage, // 存储方式localStorage/sessionStorage // 指定需要持久化的字段按需选择 paths: [token, userInfo, isLogin], }, ], }, });3.3 Pinia 实战使用登录 / 退出1. 登录时存储状态后续登录页会调用import { useUserStore } from /pinia/user; const userStore useUserStore(); // 模拟登录接口返回的token和用户信息 const mockToken eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...; const mockUserInfo { id: 1, username: admin, avatar: https://avatars.githubusercontent.com/u/123456, roles: [admin], }; // 调用Store的login方法 userStore.login(mockToken, mockUserInfo);2. 退出登录首页导航栏使用template el-button typedanger clickhandleLogout退出登录/el-button /template script setup langts import { useUserStore } from /pinia/user; const userStore useUserStore(); // 退出登录 const handleLogout () { userStore.logout(); }; /script四、Axios 封装后台系统专属版4.1 封装核心思路后台系统的 Axios 封装需满足统一基础 URL区分开发 / 生产环境请求拦截器自动添加 token、设置请求头响应拦截器统一错误处理、token 过期刷新加载状态提示Element Plus Loading封装通用请求方法get/post/put/delete4.2 完整封装实现src/api/request.tsimport axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from axios; import { ElLoading, ElMessage, ElMessageBox } from element-plus; import { useUserStore } from /pinia/user; // 定义接口返回数据类型适配后台接口规范 interface ResponseDataT any { code: number; // 业务状态码200成功401token过期500服务器错误 msg: string; // 提示信息 data: T; // 业务数据 } // 创建Axios实例 const service: AxiosInstance axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, // 环境变量中的接口地址 timeout: 10000, // 超时时间 headers: { Content-Type: application/json;charsetutf-8, }, }); // 加载状态实例 let loadingInstance: ReturnTypetypeof ElLoading.service | null null; // 请求拦截器 service.interceptors.request.use( (config: AxiosRequestConfig) { // 开启加载提示 loadingInstance ElLoading.service({ lock: true, text: 加载中..., background: rgba(0, 0, 0, 0.5), }); // 添加token到请求头 const userStore useUserStore(); if (userStore.token config.headers) { config.headers.Authorization Bearer ${userStore.token}; } return config; }, (error) { // 关闭加载提示 if (loadingInstance) loadingInstance.close(); ElMessage.error(请求发送失败 error.message); return Promise.reject(error); } ); // 响应拦截器 service.interceptors.response.use( (response: AxiosResponseResponseData) { // 关闭加载提示 if (loadingInstance) loadingInstance.close(); const { code, msg, data } response.data; // 业务状态码处理 if (code 200) { return data; // 成功直接返回业务数据 } else { // 业务错误提示 ElMessage.error(msg || 请求失败); return Promise.reject(new Error(msg || 请求失败)); } }, (error) { // 关闭加载提示 if (loadingInstance) loadingInstance.close(); // 网络错误/HTTP状态码处理 let errorMsg 请求失败; if (error.response) { // HTTP状态码处理 switch (error.response.status) { case 401: // Token过期提示并退出登录 ElMessageBox.confirm( 登录状态已过期请重新登录, 提示, { confirmButtonText: 重新登录, cancelButtonText: 取消, type: warning, } ).then(() { const userStore useUserStore(); userStore.logout(); }); errorMsg 登录状态已过期; break; case 403: errorMsg 暂无权限访问该资源; break; case 404: errorMsg 请求地址不存在; break; case 500: errorMsg 服务器内部错误请稍后重试; break; default: errorMsg error.response.data?.msg || 请求失败; } } else if (error.message.includes(timeout)) { errorMsg 请求超时请检查网络; } else if (error.message.includes(Network Error)) { errorMsg 网络异常请检查网络连接; } ElMessage.error(errorMsg); return Promise.reject(new Error(errorMsg)); } ); // 封装通用请求方法 export const request { // GET请求 getT any(url: string, params?: any, config?: AxiosRequestConfig): PromiseT { return service.get(url, { params, ...config }); }, // POST请求 postT any(url: string, data?: any, config?: AxiosRequestConfig): PromiseT { return service.post(url, data, config); }, // PUT请求 putT any(url: string, data?: any, config?: AxiosRequestConfig): PromiseT { return service.put(url, data, config); }, // DELETE请求 deleteT any(url: string, params?: any, config?: AxiosRequestConfig): PromiseT { return service.delete(url, { params, ...config }); }, }; export default service;4.3 配置环境变量.env.development# 开发环境接口地址根据实际后端地址修改 VITE_API_BASE_URL http://localhost:3000/api # 项目基础路径 VITE_BASE_URL /4.4 封装业务接口src/api/user.tsimport { request } from ./request; // 定义登录接口参数类型 export interface LoginParams { username: string; password: string; } // 定义用户信息返回类型 export interface LoginResponse { token: string; userInfo: { id: number; username: string; avatar: string; roles: string[]; }; } // 登录接口 export const loginApi (params: LoginParams) { return request.postLoginResponse(/user/login, params); }; // 获取用户信息接口 export const getUserInfoApi () { return request.getLoginResponse[userInfo](/user/info); };五、Element Plus 开发后台核心页面5.1 Element Plus 按需引入配置vite.config.ts为减少打包体积配置按需引入import { defineConfig } from vite; import vue from vitejs/plugin-vue; import path from path; // Element Plus按需引入插件 import AutoImport from unplugin-auto-import/vite; import Components from unplugin-vue-components/vite; import { ElementPlusResolver } from unplugin-vue-components/resolvers; // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), // 自动导入Element Plus API AutoImport({ resolvers: [ElementPlusResolver()], }), // 自动导入Element Plus组件 Components({ resolvers: [ElementPlusResolver()], }), ], resolve: { // 配置别名 alias: { : path.resolve(__dirname, src), }, }, // 开发服务器配置跨域代理 server: { port: 3001, // 前端端口 proxy: { // 代理接口请求解决跨域 /api: { target: http://localhost:3000, // 后端接口地址 changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ), }, }, }, });5.2 登录页开发src/views/Login/index.vue包含表单验证、登录逻辑、整合 Pinia 和 Axiostemplate div classlogin-container div classlogin-card h2 classlogin-title后台管理系统登录/h2 el-form refloginFormRef :modelloginForm :rulesloginRules label-width80px classlogin-form el-form-item label用户名 propusername el-input v-modelloginForm.username placeholder请输入用户名 prefix-iconel-icon-user / /el-form-item el-form-item label密码 proppassword el-input v-modelloginForm.password typepassword placeholder请输入密码 prefix-iconel-icon-lock / /el-form-item el-form-item label验证码 propcode el-input v-modelloginForm.code placeholder请输入验证码 stylewidth: 60%; display: inline-block / div classcode-img !-- 验证码图片模拟 -- img srchttps://picsum.photos/100/40 alt验证码 clickrefreshCode / /div /el-form-item el-form-item el-button typeprimary classlogin-btn clickhandleLogin :loadingloading 登录 /el-button /el-form-item /el-form /div /div /template script setup langts import { ref } from vue; import { useRouter } from vue-router; import type { FormInstance, FormRules } from element-plus; import { ElMessage } from element-plus; import { useUserStore } from /pinia/user; import { loginApi, type LoginParams } from /api/user; // 表单Ref const loginFormRef refFormInstance(); // 加载状态 const loading ref(false); // 路由实例 const router useRouter(); // 用户Store const userStore useUserStore(); // 登录表单数据 const loginForm refLoginParams { code: string }({ username: , password: , code: , }); // 表单验证规则 const loginRules refFormRules({ username: [ { required: true, message: 请输入用户名, trigger: blur }, { min: 3, max: 20, message: 用户名长度为3-20位, trigger: blur }, ], password: [ { required: true, message: 请输入密码, trigger: blur }, { min: 6, max: 20, message: 密码长度为6-20位, trigger: blur }, ], code: [ { required: true, message: 请输入验证码, trigger: blur }, { len: 4, message: 验证码长度为4位, trigger: blur }, ], }); // 刷新验证码 const refreshCode () { // 模拟刷新验证码修改图片URL const codeImg document.querySelector(.code-img img) as HTMLImageElement; codeImg.src https://picsum.photos/100/40?${Date.now()}; }; // 登录逻辑 const handleLogin async () { if (!loginFormRef.value) return; try { // 表单验证 await loginFormRef.value.validate(); loading.value true; // 调用登录接口 const res await loginApi({ username: loginForm.value.username, password: loginForm.value.password, }); // 存储登录状态到Pinia userStore.login(res.token, res.userInfo); // 登录成功提示 ElMessage.success(登录成功); // 跳转到首页 router.push(/home); } catch (error) { console.error(登录失败, error); ElMessage.error(登录失败请检查账号密码); } finally { loading.value false; } }; /script style scoped langscss .login-container { width: 100vw; height: 100vh; background: linear-gradient(120deg, #409eff, #67c23a); display: flex; justify-content: center; align-items: center; } .login-card { width: 400px; padding: 30px; background-color: #fff; border-radius: 8px; box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); } .login-title { text-align: center; color: #409eff; margin-bottom: 20px; font-size: 20px; } .login-form { margin-top: 20px; } .code-img { display: inline-block; margin-left: 10px; img { cursor: pointer; border-radius: 4px; } } .login-btn { width: 100%; height: 40px; font-size: 16px; } /style5.3 后台首页开发布局 导航菜单步骤 1创建布局组件src/layouts/MainLayout.vuetemplate el-container styleheight: 100vh; !-- 侧边栏 -- el-aside width200px stylebackground-color: #2e3b4e; div classlogo img srchttps://picsum.photos/40/40 altlogo / span后台管理系统/span /div !-- 导航菜单 -- el-menu default-active1 classel-menu-vertical-demo background-color#2e3b4e text-color#fff active-text-color#ffd04b selecthandleMenuSelect el-menu-item index1 el-iconHouse //el-icon template #title首页/template /el-menu-item el-menu-item index2 el-iconList //el-icon template #title数据列表/template /el-menu-item el-sub-menu index3 template #title el-iconSetting //el-icon span系统设置/span /template el-menu-item index3-1用户管理/el-menu-item el-menu-item index3-2角色管理/el-menu-item /el-sub-menu /el-menu /el-aside !-- 主内容区 -- el-container !-- 顶部导航 -- el-header styletext-align: right; font-size: 12px el-dropdown i classel-icon-setting stylemargin-right: 15px/i template #dropdown el-dropdown-menu el-dropdown-item查看/el-dropdown-item el-dropdown-item新增/el-dropdown-item el-dropdown-item删除/el-dropdown-item /el-dropdown-menu /template /el-dropdown span欢迎您{{ userStore.userInfo?.username }}/span el-button typetext clickhandleLogout stylemargin-left: 10px退出/el-button /el-header !-- 内容区域路由出口 -- el-main router-view / /el-main /el-container /el-container /template script setup langts import { useRouter } from vue-router; import { useUserStore } from /pinia/user; import { ElMessage } from element-plus; // 引入Element Plus图标 import { House, List, Setting } from element-plus/icons-vue; const router useRouter(); const userStore useUserStore(); // 菜单选择事件 const handleMenuSelect (index: string) { switch (index) { case 1: router.push(/home); break; case 2: router.push(/list); break; case 3-1: ElMessage.info(用户管理功能开发中...); break; case 3-2: ElMessage.info(角色管理功能开发中...); break; } }; // 退出登录 const handleLogout () { userStore.logout(); }; /script style scoped langscss .el-header { background-color: #fff; color: #333; line-height: 60px; border-bottom: 1px solid #e6e6e6; } .el-aside { color: #333; .logo { height: 60px; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 16px; border-bottom: 1px solid #404854; img { width: 40px; height: 40px; border-radius: 50%; margin-right: 10px; } } } .el-main { background-color: #f5f7fa; color: #333; padding: 20px; } .el-menu-vertical-demo { border-right: none; height: calc(100vh - 60px); } /style步骤 2修改首页组件src/views/Home/index.vuetemplate div classhome-content el-card title后台概览 classhome-card el-row :gutter20 el-col :span6 div classstat-card p classstat-title今日访问量/p p classstat-value1,234/p /div /el-col el-col :span6 div classstat-card p classstat-title今日订单数/p p classstat-value567/p /div /el-col el-col :span6 div classstat-card p classstat-title今日销售额/p p classstat-value¥89,012/p /div /el-col el-col :span6 div classstat-card p classstat-title用户总数/p p classstat-value10,890/p /div /el-col /el-row /el-card !-- 快速操作 -- el-card title快速操作 classhome-card stylemargin-top: 20px; el-button typeprimary iconel-icon-plus新增数据/el-button el-button typesuccess iconel-icon-download导出数据/el-button el-button typewarning iconel-icon-upload导入数据/el-button el-button typeinfo iconel-icon-refresh刷新数据/el-button /el-card /div /template script setup langts // 首页业务逻辑可后续扩展 /script style scoped langscss .home-content { width: 100%; } .home-card { box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .stat-card { background: linear-gradient(120deg, #e53935, #e35d5b); padding: 20px; border-radius: 8px; color: #fff; text-align: center; .stat-title { font-size: 14px; margin-bottom: 10px; opacity: 0.8; } .stat-value { font-size: 24px; font-weight: bold; } } // 不同卡片不同渐变 .el-col:nth-child(2) .stat-card { background: linear-gradient(120deg, #43a047, #4caf50); } .el-col:nth-child(3) .stat-card { background: linear-gradient(120deg, #1e88e5, #2196f3); } .el-col:nth-child(4) .stat-card { background: linear-gradient(120deg, #fdd835, #ffc107); } /style步骤 3修改路由配置让首页使用布局组件修改src/router/index.ts中的首页路由{ path: /home, name: Home, // 布局组件作为父组件首页内容作为子路由 component: () import(/layouts/MainLayout.vue), children: [ { path: , // 子路由默认路径 component: () import(/views/Home/index.vue), }, ], meta: { requiresAuth: true, title: 后台首页, }, }, { path: /list, name: List, component: () import(/layouts/MainLayout.vue), children: [ { path: , component: () import(/views/List/index.vue), }, ], meta: { requiresAuth: true, title: 数据列表页, }, },六、实战踩坑与解决方案6.1 常见问题及解决问题场景原因分析解决方案路由跳转白屏路由懒加载路径错误或布局组件无路由出口1. 检查 import 路径是否正确2. 确保布局组件中有router-view /Pinia 持久化失效未安装 pinia-plugin-persistedstate或配置错误1. 确认安装插件并注册2. 检查 persist 配置中的 paths 是否包含需要持久化的字段Axios 请求跨域开发环境未配置代理或生产环境后端未配置 CORS1. Vite 配置 proxy 代理见 vite.config.ts2. 后端配置 Access-Control-Allow-OriginElement Plus 图标不显示未按需引入图标或版本不兼容1. 手动引入需要的图标如 import {House} from element-plus/icons-vue2. 确保 Element Plus 版本≥2.3.0登录后刷新页面状态丢失Pinia 未开启持久化或 token 未存储到 localStorage启用 pinia-plugin-persistedstate配置 persist 存储到 localStorage6.2 性能优化建议路由懒加载所有页面均使用() import(/views/xxx/index.vue)懒加载减少首屏加载体积Element Plus 按需引入通过 unplugin-vue-components 插件自动按需引入组件避免全局引入体积过大Axios 取消重复请求添加取消请求逻辑避免同一接口多次请求导致数据错乱图片懒加载首页统计卡片的图片使用v-lazy指令懒加载需安装 vue3-lazy。七、总结与进阶建议7.1 核心总结路由管理通过 Vue Router 实现多页面导航结合路由守卫控制登录权限参数传递支持 params/query 两种方式状态管理Pinia 替代 Vuex轻量且支持持久化完美适配后台系统的用户状态管理请求封装Axios 拦截器统一处理 token、错误、加载状态封装通用请求方法提升开发效率UI 开发Element Plus 提供丰富的后台组件结合布局组件快速搭建后台系统页面结构。7.2 进阶建议权限控制基于 Pinia 的用户角色实现路由 / 按钮级别的权限控制国际化集成 vue-i18n 实现多语言切换适配海外后台系统主题定制基于 Element Plus 的主题变量实现动态主题切换错误监控集成 Sentry 捕获前端错误提升系统稳定性打包优化配置 Vite 的 build 选项压缩代码、拆分 chunk减少生产环境包体积。最后本文从 0 到 1 整合了 Vue3 生态的核心工具实现了一个具备完整登录流程、多页面导航、状态管理、请求封装的后台管理系统核心模块。内容覆盖基础配置、核心逻辑、踩坑解决方案既适合 Vue3 新手入门实战也能为企业级后台项目开发提供参考。如果对你有帮助欢迎点赞 收藏 关注后续会持续更新 Vue3 后台系统进阶内容如权限控制、国际化、打包优化。如果有任何问题或不同见解欢迎在评论区交流哦

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2419608.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…