<think>我们根据用户需求,使用Vite创建Vue3项目,并集成所需技术栈。
步骤:
1. 创建项目
2. 安装依赖
3. 配置路由(Vue Router)
4. 配置状态管理(Vuex或Pinia,这里以Pinia为例,因为Vue3推荐使用Pinia)
5. 配置Ant Design Vue(Antdv)组件库
6. 配置Mockjs模拟数据
7. 封装axios
8. 实现登录功能
9. 实现产品列表和产品上传页面
10. 配置路由守卫进行权限控制
注意:用户指定Node版本为v14.17.2或v16.20.2,请确保使用符合版本的Node。
我们开始逐步实现:
步骤1:创建项目
使用Vite创建项目,选择vue-ts模板。
步骤2:安装依赖
安装项目所需的依赖:vue-router, pinia, ant-design-vue, mockjs, axios
步骤3:配置路由
创建路由实例,定义路由规则。
步骤4:配置Pinia
创建Pinia实例并注册到应用。
步骤5:配置Antdv
全局引入Antdv组件或按需引入(这里为了简便,全局引入)。
步骤6:配置Mockjs
在开发环境下拦截API请求,返回模拟数据。
步骤7:封装axios
创建axios实例,设置请求拦截器和响应拦截器。
步骤8:实现登录功能
创建登录页面,调用登录接口,保存token,并跳转到主页。
步骤9:创建产品列表和产品上传页面
使用Antdv组件创建两个页面。
步骤10:配置路由守卫
在路由跳转前验证用户是否登录(通过检查token),未登录则跳转到登录页。
下面我们具体展开:
1. 创建项目
```bash
npm create vite@latest my-vue-project -- --template vue-ts
cd my-vue-project
```
2. 安装依赖
```bash
npm install vue-router@4 pinia ant-design-vue@next mockjs axios
```
3. 配置路由
在src目录下创建router/index.ts文件:
```ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/login'
},
{
path: '/login',
name: 'Login',
component: () => import('@/views/Login.vue')
},
{
path: '/home',
name: 'Home',
component: () => import('@/views/Home.vue'),
children: [
{
path: '/products',
name: 'ProductList',
component: () => import('@/views/ProductList.vue')
},
{
path: '/upload',
name: 'ProductUpload',
component: () => import('@/views/ProductUpload.vue')
}
]
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
```
4. 配置Pinia
在src目录下创建store/index.ts:
```ts
import { createPinia } from 'pinia'
const pinia = createPinia()
export default pinia
```
然后在main.ts中注册:
```ts
import { createApp } from 'vue'
import App from './App.vue'
import pinia from './store'
import router from './router'
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
const app = createApp(App)
app.use(pinia)
app.use(router)
app.use(Antd)
app.mount('#app')
```
5. 配置Mockjs
在src目录下创建mock/index.ts:
```ts
import Mock from 'mockjs'
Mock.mock('/api/login', 'post', (options: any) => {
const { username, password } = JSON.parse(options.body)
if (username === 'admin' && password === '123456') {
return {
code: 200,
message: '登录成功',
data: {
token: 'mock_token'
}
}
} else {
return {
code: 401,
message: '用户名或密码错误'
}
}
})
// 其他模拟接口...
```
在main.ts中引入mock(注意:只在开发环境使用)
```ts
if (import.meta.env.MODE === 'development') {
import('./mock')
}
```
6. 封装axios
在src/utils/request.ts中:
```ts
import axios from 'axios'
import { message } from 'ant-design-vue'
const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 5000
})
service.interceptors.request.use(
config => {
// 在请求头中添加token
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
return config
},
error => {
return Promise.reject(error)
}
)
service.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 200) {
message.error(res.message || 'Error')
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
error => {
message.error(error.message || '请求失败')
return Promise.reject(error)
}
)
export default service
```
7. 实现登录功能
创建src/views/Login.vue:
```vue
<template>
<div class="login-container">
<a-form :model="form" @submit="handleSubmit">
<a-form-item label="用户名">
<a-input v-model:value="form.username" />
</a-form-item>
<a-form-item label="密码">
<a-input v-model:value="form.password" type="password" />
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit">登录</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive } from 'vue'
import { useRouter } from 'vue-router'
import request from '@/utils/request'
export default defineComponent({
setup() {
const form = reactive({
username: '',
password: ''
})
const router = useRouter()
const handleSubmit = async () => {
try {
const res = await request.post('/api/login', form)
if (res.code === 200) {
localStorage.setItem('token', res.data.token)
router.push('/home')
}
} catch (error) {
console.error(error)
}
}
return {
form,
handleSubmit
}
}
})
</script>
```
8. 创建产品列表和产品上传页面
产品列表页面:src/views/ProductList.vue
```vue
<template>
<div>
<h1>产品列表</h1>
<!-- 使用Antdv表格展示产品列表 -->
</div>
</template>
```
产品上传页面:src/views/ProductUpload.vue
```vue
<template>
<div>
<h1>产品上传</h1>
<!-- 上传表单 -->
</div>
</template>
```
9. 配置路由守卫
在router/index.ts中:
```ts
// ...其他代码
router.beforeEach((to, from, next) => {
if (to.path === '/login') {
next()
} else {
const token = localStorage.getItem('token')
if (!token) {
next('/login')
} else {
next()
}
}
})
```
10. 修改App.vue,添加路由视图
```vue
<template>
<router-view />
</template>
```
以上为基本框架搭建步骤,根据需求可进一步细化页面和功能。
注意:由于使用了TypeScript,需要配置好类型定义。另外,Antdv组件按需引入可以减小打包体积,但此处为简便全局引入。
最后,确保在package.json中指定了正确的Node版本:
```json
"engines": {
"node": "14.17.2 || 16.20.2"
}
```
这样,我们就搭建了一个包含登录功能、产品列表和产品上传菜单的后台系统基础框架。
§§相关问题§§
1. 如何按需引入Ant Design Vue组件以优化打包体积?
2. 在Vite中如何配置环境变量?
3. Pinia和Vuex有什么区别?为什么Vue3推荐使用Pinia?
4. 如何实现Mockjs在开发环境启用,生产环境禁用?
5. 如何封装axios以实现统一错误处理和请求拦截?
</think>
### 使用 Vite 搭建 Vue3 后台管理系统框架
#### 1. **环境准备**
- 确保 Node.js 版本 `v14.17.2` 或 `v16.20.2`:
```bash
node -v # 验证版本
```
#### 2. **创建项目**
```bash
npm create vite@latest vue3-admin -- --template vue-ts
cd vue3-admin
npm install
```
#### 3. **安装依赖**
```bash
npm install vue-router@4 pinia ant-design-vue@3.x mockjs axios
# 或使用 Vuex(二选一)
# npm install vuex@4
```
#### 4. **目录结构**
```
src/
├── api/ # API 封装
│ └── request.ts # Axios 封装
├── store/ # Pinia/Vuex
│ └── user.ts # 用户状态管理
├── router/ # 路由
│ └── index.ts
├── views/ # 页面组件
│ ├── Login.vue # 登录页
│ ├── ProductList.vue # 产品列表
│ └── ProductUpload.vue # 产品上传
├── mock/ # Mock 数据
│ └── index.ts
└── main.ts # 入口文件
```
#### 5. **核心配置**
##### (1) Axios 封装 (`src/api/request.ts`)
```typescript
import axios from "axios";
const service = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 5000,
});
// 请求拦截器(添加 token)
service.interceptors.request.use(config => {
const token = localStorage.getItem("token");
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
// 响应拦截器(错误处理)
service.interceptors.response.use(
response => response.data,
error => {
console.error("API Error:", error);
return Promise.reject(error);
}
);
export default service;
```
##### (2) Pinia 状态管理 (`src/store/user.ts`)
```typescript
import { defineStore } from "pinia";
export const useUserStore = defineStore("user", {
state: () => ({
token: localStorage.getItem("token") || "",
}),
actions: {
setToken(token: string) {
this.token = token;
localStorage.setItem("token", token);
},
logout() {
this.token = "";
localStorage.removeItem("token");
},
},
});
```
##### (3) 路由配置 (`src/router/index.ts`)
```typescript
import { createRouter, createWebHistory } from "vue-router";
import Login from "../views/Login.vue";
const routes = [
{
path: "/login",
name: "Login",
component: Login,
},
{
path: "/",
redirect: "/products",
meta: { requiresAuth: true },
children: [
{
path: "/products",
name: "ProductList",
component: () => import("../views/ProductList.vue"),
},
{
path: "/upload",
name: "ProductUpload",
component: () => import("../views/ProductUpload.vue"),
},
],
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
// 路由守卫
router.beforeEach((to, from, next) => {
const isAuthenticated = !!localStorage.getItem("token");
if (to.meta.requiresAuth && !isAuthenticated) {
next("/login");
} else {
next();
}
});
export default router;
```
##### (4) Mock 数据 (`src/mock/index.ts`)
```typescript
import Mock from "mockjs";
Mock.mock("/api/login", "post", {
code: 200,
data: {
token: "@guid",
username: "admin",
},
});
Mock.mock("/api/products", "get", {
"list|10": [{
id: "@id",
name: "@ctitle",
price: "@float(10,1000,2,2)",
}],
});
```
#### 6. **登录功能实现 (`src/views/Login.vue`)**
```vue
<template>
<a-form @finish="handleLogin">
<a-form-item label="账号">
<a-input v-model:value="form.username" />
</a-form-item>
<a-form-item label="密码">
<a-input-password v-model:value="form.password" />
</a-form-item>
<a-button type="primary" html-type="submit">登录</a-button>
</a-form>
</template>
<script setup lang="ts">
import { reactive } from "vue";
import { useRouter } from "vue-router";
import request from "../api/request";
import { useUserStore } from "../store/user";
const form = reactive({ username: "", password: "" });
const router = useRouter();
const userStore = useUserStore();
const handleLogin = async () => {
const res = await request.post("/api/login", form);
userStore.setToken(res.data.token);
router.push("/products");
};
</script>
```
#### 7. **产品列表页 (`src/views/ProductList.vue`)**
```vue
<template>
<a-table :columns="columns" :data-source="products">
<template #headerCell="{ column }">
{{ column.title }}
</template>
</a-table>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import request from "../api/request";
const products = ref([]);
const columns = [
{ title: "ID", dataIndex: "id" },
{ title: "产品名", dataIndex: "name" },
{ title: "价格", dataIndex: "price" },
];
onMounted(async () => {
const res = await request.get("/api/products");
products.value = res.list;
});
</script>
```
#### 8. **入口文件配置 (`src/main.ts`)**
```typescript
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import { createPinia } from "pinia";
import Antd from "ant-design-vue";
import "ant-design-vue/dist/antd.css";
// Mock 仅开发环境启用
if (import.meta.env.DEV) {
import("./mock");
}
const app = createApp(App);
app.use(router);
app.use(createPinia());
app.use(Antd);
app.mount("#app");
```
### 关键功能说明
1. **路由守卫**:通过路由元信息 `requiresAuth` 控制页面访问权限[^2]
2. **Mock 拦截**:开发环境模拟登录和产品数据接口[^2]
3. **组件库集成**:Ant Design Vue 提供 UI 组件支持
4. **状态管理**:Pinia 管理用户登录状态(也可替换为 Vuex)
5. **API 封装**:Axios 统一处理请求/响应拦截
### 启动项目
```bash
npm run dev
```