前言
默认大家看完前面的node后端搭建,这章开始写后台系统。主要技术栈: vue3+Element Plus。
项目信息
基本结构
src/
├── api/ # API 请求
├── assets/ # 静态资源
├── components/ # 公共组件
├── layouts/ # 布局组件
├── router/ # 路由配置
├── stores/ # 状态管理
├── styles/ # 全局样式
├── utils/ # 工具函数
└── views/ # 页面组件
搭建项目
npm create vite@latest
创建项目完成,因为是学习我就选了一个ts,现在安装必要的依赖
# 安装主要依赖
npm install vue-router pinia axios element-plus vue-i18n @element-plus/icons-vue
# 安装开发依赖
npm install -D sass
测试
npm run dev
运行命令

页面执行http://localhost:5173/

到这里项目安装完成,接下来编写系统必要文件代码
环境变量
创建.env
# .env
VITE_API_BASE_URL=http://localhost:3000
VITE_APP_TITLE=Blog Admin Dev
VITE_APP_ENV=development
创建.env.development
# 生产环境配置
VITE_API_BASE_URL=https://api.yourdomain.com
VITE_APP_TITLE=Blog Admin
VITE_APP_ENV=production
在gitignore中加入
# Environment
.env
.env.*
路由
创建路由配置文件src/router/index.js
/**
* 导入 vue-router 相关函数
*/
import { createRouter, createWebHistory } from 'vue-router'
/**
* 定义路由配置
*/
const routes = [
{
path: '/login', // 登录页面路径
name: 'login', // 路由名称
component: () => import('@/views/login/index.vue'), // 懒加载登录组件
meta: {
title: '登录', // 页面标题
hidden: true // 是否在导航菜单中隐藏
}
},
{
path: '/', // 根路径
component: () => import('@/layouts/index.vue'), // 懒加载布局组件
redirect: '/dashboard', // 重定向到仪表盘
children: [ // 子路由配置
{
path: 'dashboard', // 仪表盘路径
name: 'dashboard', // 路由名称
component: () => import('@/views/dashboard/index.vue'), // 懒加载仪表盘组件
meta: {
title: '首页', // 页面标题
icon: 'House' // 导航菜单图标
}
}
]
}
]
/**
* 创建路由实例
*/
const router = createRouter({
history: createWebHistory(), // 使用 HTML5 History 模式
routes // 路由配置
})
/**
* 路由守卫
* 用于处理路由权限和页面标题
*/
router.beforeEach((to, from, next) => {
// 设置页面标题
document.title = to.meta.title ? `${to.meta.title} - 博客管理系统` : '博客管理系统'
// 判断权限
const token = localStorage.getItem('token')
if (to.token !== '/login' && !token) {
next({ path: '/login' })
} else {
next()
}
})
export default router
pinia
创建文件 src/stores/user.js
/**
* 导入 Pinia 的 defineStore 函数
*/
import { defineStore } from 'pinia'
/**
* 定义用户状态管理 store
*/
const useUserStore = defineStore('user', {
/**
* 定义状态
*/
state: () => ({
token: localStorage.getItem('token') || '', // 用户令牌
userInfo: localStorage.getItem('userInfo') || null // 用户信息
}),
/**
* 定义 getter
*/
getters: {
getToken: (state) => state.token, // 获取用户令牌
getUserInfo: (state) => state.userInfo // 获取用户信息
},
/**
* 定义 actions
*/
actions: {
/**
* 设置用户令牌
* @param {string} token - 用户令牌
*/
setToken(token) {
this.token = token
localStorage.setItem('token', token)
},
/**
* 设置用户信息
* @param {Object} userInfo - 用户信息对象
*/
setUserInfo(userInfo) {
this.userInfo = userInfo
localStorage.setItem('userInfo', userInfo)
},
/**
* 清除用户信息
*/
clearUserInfo() {
this.token = ''
this.userInfo = null
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
}
}
})
export default useUserStore
请求
创建http请求工具src/utils/request.js
import axios from 'axios'
import router from '@/router'
import useUserStore from '@/stores/user'
import { ElMessage } from 'element-plus'
// 创建axios实例
const service = axios.create({
baseURL: import.meta.env.VITE_APP_API_BASE_URL,
timeout: 5000,
headers: {
'Content-Type': 'application/json'
}
})
// 请求拦截器
service.interceptors.request.use(
config => {
// 从 localStorage 获取 token
const token = localStorage.getItem('token')
if (token) {
// 让每个请求携带token
config.headers['Authorization'] = `Bearer ${token}`
}
return config
},
error => {
console.error('请求错误:', error)
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
const res = response.data
// 如果返回的状态码不是200,说明接口请求有误
if (res.code !== 200) {
ElMessage({
message: res.message || '系统错误',
type: 'error',
duration: 5 * 1000
})
// 401: 未登录或token过期
if (res.code === 401) {
const userStore = useUserStore()
userStore.clearUserInfo()
router.push('/login')
}
return Promise.reject(new Error(res.message || '系统错误'))
} else {
return res
}
},
error => {
console.error('响应错误:', error)
ElMessage({
message: error.message || '请求失败',
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
API模块
创建src/api/user.js
// src/api/user.js
import request from '@/utils/request'
export function login(data) {
return request({
url: '/user/login',
method: 'post',
data
})
}
export function getUserInfo() {
return request({
url: '/user/info',
method: 'get'
})
}
main文件更新
// 导入样式
import 'element-plus/dist/index.css' // Element Plus 样式
import './styles/index.scss' // 全局样式
// 导入 Vue 相关
import { createApp } from 'vue'
import App from './App.vue'
// 导入路由
import router from './router'
// 导入状态管理
import { createPinia } from 'pinia'
const pinia = createPinia()
// 导入 Element Plus
import ElementPlus from 'element-plus'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
// 创建应用实例
const app = createApp(App)
// 注册 Element Plus 图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
// 全局错误处理
app.config.errorHandler = (err, vm, info) => {
console.error('全局错误:', err)
console.error('错误信息:', info)
}
// 使用插件
app.use(router)
app.use(pinia)
app.use(ElementPlus)
// 挂载应用
app.mount('#app')
布局样式
创建全局样式文件src/styles/index.scss
// 重置样式
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
// 滚动条样式
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
配置跨域
在vite.config.js文件
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:3000', // 后端服务器地址
changeOrigin: true, // 允许跨域
rewrite: (path) => path.replace(/^\/api/, '') // 重写路径
}
}
}
})
布局文件
创建文件src/layouts/index.vue
<script setup>
import { ref } from 'vue'
</script>
<template>
<div class="app-wrapper">
<!-- 布局内容将在这里实现 -->
</div>
</template>
<style lang="scss" scoped>
.app-wrapper {
position: relative;
height: 100%;
width: 100%;
}
</style>
OK,基础架构应该差不多了。以后想到在来补充把
1158

被折叠的 条评论
为什么被折叠?



