博客项目全栈开发 后台管理系统 第一章:项目搭建

前言

默认大家看完前面的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,基础架构应该差不多了。以后想到在来补充把

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值