系列文档目录
Vue3+Vite+TypeScript安装
Element Plus安装与配置
主页设计与router配置
静态菜单设计
Pinia引入
Header响应式菜单缩展
Mockjs引用与Axios封装
登录设计
登录成功跳转主页
多用户动态加载菜单
Pinia持久化
动态路由-配置
文章目录
目录
系列文档目录
文章目录
前言
一、Mockjs
二、axios
三、Aside调整
参考文献:
前言
Mock.js 是一个用于模拟数据的 JavaScript 库。它可以帮助开发者快速生成模拟数据,用于前端开发中的接口测试、页面展示等场景,从而避免因后端接口未完成而导致前端开发进度受阻。
Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js 环境。它提供了简单易用的 API,用于发送各种类型的 HTTP 请求,如 GET、POST、PUT、DELETE 等。
一、Mockjs
1.安装
npm install mockjs
2.mock模拟数据构建
2.1在src文件夹下新建文件夹mock
2.2在mock文件夹下新建文件夹mockData
2.3在mockData文件夹下新建文件menu.ts
完整代码:
// src/mock/mockData/menu.ts
import Mock from 'mockjs';
import { Document, Setting } from '@element-plus/icons-vue'; // 假设你使用的是 Element Plus 的图标
// 模拟菜单数据
const menuData = Mock.mock({
data: [
{ index: 'Home', label: '首页', icon: Document },
{
index: 'SysSettings',
label: '系统设置',
icon: Setting,
children: [
{ index: 'UserInfo', label: '个人资料' },
{ index: 'AccountSetting', label: '账户设置' },
],
},
],
});
export default menuData;
2.4.在mock文件夹下新建文件index.ts
// src/mock/index.ts
import Mock from 'mockjs';
import menuData from '@/mock/mockData/menuData';
Mock.mock(/menu/, 'get', (req: any) => {
return menuData.data;
});
3.Mock引用
3.1修改main.ts文件
重点部分:
// 引入 Mock 数据
import './mock'
完整代码:
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import router from './router';
import App from './App.vue'
import { createPinia } from 'pinia'
// 引入 Mock 数据
import './mock'
// 创建 Pinia 实例
const pinia = createPinia();
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(ElementPlus, {
locale: zhCn,
})
app.use(router);
app.use(pinia);
app.mount('#app')
二、axios
1.安装
npm install axios
2.axios封装
2.1在src文件夹下新建文件夹api
2.2在api下新建文件request.ts
在baseURL: 'http://127.0.0.1:5173' 根据实际情况填写,具体参考运行时候网址,待api数据可以的时候调整baseURL指向可以无缝隙切换(后续会增加正式区与测试区自行切换)
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
// 创建 axios 实例
const instance: AxiosInstance = axios.create({
baseURL: 'http://127.0.0.1:5173', // 'http://127.0.0.1:8080', // 替换为你的 API 基础地址
timeout: 5000, // 请求超时时间
headers: {
'Content-Type': 'application/json'
}
});
// 请求拦截器
instance.interceptors.request.use(
(config: AxiosRequestConfig) => {
// 在发送请求之前做些什么,例如添加 Token
const token = localStorage.getItem('token'); // 假设使用 localStorage 存储 token
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
// 增加 API KEY
const api_key = 'Test';
if (api_key) {
config.headers['api_key'] = `${api_key}`;
}
return config;
},
(error: AxiosError) => {
// 对请求错误做些什么
console.error('请求错误:', error);
return Promise.reject(error);
}
);
// 响应拦截器
instance.interceptors.response.use(
(response: AxiosResponse) => {
// 对响应数据做点什么
return response.data; // 直接返回数据
},
(error: AxiosError) => {
// 对响应错误做点什么
if (error.response) {
// 请求已发出,但服务器响应的状态码不在 2xx 范围内
console.error('响应错误:', error.response);
return Promise.reject(error.response.data); // 返回后端返回的错误数据
} else if (error.request) {
// 请求已发出,但没有收到响应
console.error('请求错误:', error.request);
return Promise.reject(error.request);
} else {
// 在设置请求时触发了错误
console.error('请求设置错误:', error.message);
return Promise.reject(error.message);
}
}
);
// 封装常用的请求方法
export const request = (url: string, data: any = {}, method: string = 'GET') => {
return instance({
url,
method,
data: method.toUpperCase() === 'GET' ? null : data,
params: method.toUpperCase() === 'GET' ? data : null
});
};
// 封装 POST 请求
export const post = (url: string, data: any = {}) => {
return request(url, data, 'POST');
};
// 封装 GET 请求
export const get = (url: string, params: any = {}) => {
return request(url, params, 'GET');
};
在该项目中,表头新增api_key,可以根据实际情况进行删除或保留
// 增加 API KEY
const api_key = 'Test';
if (api_key) {
config.headers['api_key'] = `${api_key}`;
}
2.3在api文件夹下新建menu.ts
// 引入 request、post 和 get 函数
import { get } from '@/api/request'; // 绝对路径
// 登录接口
export const menuAPI = async () => {
try {
const result = await get('/menu'); // 使用封装的 get 方法
return result ;
} catch (error) {
console.error('获取菜单数据失败:', error);
return [];
}
};
三、Aside调整
1.调整MainAsideCont.vue
menu数据原是引用固定,现改为引用api资料
/*
const menuData = ref<MenuItem[]>([
{ index: 'Home', label: '首页', icon: Document },
{
index: 'SysSettings',
label: '系统设置',
icon: Setting,
children: [
{ index: 'UserInfo', label: '个人资料' },
{ index: 'AccountSetting', label: '账户设置' },
],
},
]);
*/
// 确保 menuAPI 是一个数组,并赋值给 menuData
const menuData = ref<MenuItem[]>([]); // 初始化为空数组
// 封装数据获取和处理逻辑
const fetchMenuData = async () => {
try {
const result = await menuAPI(); // 调用异步 API 获取数据
console.error('menuAPI :', result);
if (Array.isArray(result)) {
menuData.value = result as MenuItem[]; // 确保结果是 MenuItem[] 类型
} else {
console.error('menuAPI 返回的数据不是数组:', result);
}
} catch (error) {
console.error('获取菜单数据失败:', error);
}
};
完整代码:
<template>
<el-menu
:default-active="activeIndex"
class="el-menu-vertical-demo"
:collapse="isCollapse"
>
<h3 :key="TitleText">{{TitleText}}</h3>
<!-- 渲染没有子菜单的项 -->
<el-menu-item
v-for="item in noChilden"
:key="item.index"
:index="item.index"
@click="handlemenu(item)"
>
<component class="icon" :is="item.icon"></component>
<span>{{ item.label }}</span>
</el-menu-item>
<!-- 渲染有子菜单的项 -->
<el-sub-menu
v-for="item in hasChilden"
:key="item.index"
:index="item.index"
>
<template #title>
<component class="icon" :is="item.icon"></component>
<span>{{ item.label }}</span>
</template>
<el-menu-item
v-for="subItem in item.children"
:key="subItem.index"
:index="subItem.index"
@click="handlemenuchild(item, subItem)"
>
<span>{{ subItem.label }}</span>
</el-menu-item>
</el-sub-menu>
</el-menu>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue';
import { defineComponent } from 'vue';
import { useRouter } from 'vue-router';
import {
Document,
Setting,
} from '@element-plus/icons-vue';
import { useAllDataStore } from '@/stores';
import { menuAPI } from '@/api/menu';
interface MenuItem {
index: string;
label: string;
icon?: any;
children?: MenuItem[];
}
/*
const menuData = ref<MenuItem[]>([
{ index: 'Home', label: '首页', icon: Document },
{
index: 'SysSettings',
label: '系统设置',
icon: Setting,
children: [
{ index: 'UserInfo', label: '个人资料' },
{ index: 'AccountSetting', label: '账户设置' },
],
},
]);
*/
// 确保 menuAPI 是一个数组,并赋值给 menuData
const menuData = ref<MenuItem[]>([]); // 初始化为空数组
// 封装数据获取和处理逻辑
const fetchMenuData = async () => {
try {
const result = await menuAPI(); // 调用异步 API 获取数据
console.error('menuAPI :', result);
if (Array.isArray(result)) {
menuData.value = result as MenuItem[]; // 确保结果是 MenuItem[] 类型
} else {
console.error('menuAPI 返回的数据不是数组:', result);
}
} catch (error) {
console.error('获取菜单数据失败:', error);
}
};
onMounted(() => {
fetchMenuData(); // 在组件挂载时调用 fetchMenuData 函数
});
const hasChilden = computed(() => menuData.value.filter(item => item.children && item.children.length > 0));
const noChilden = computed(() => menuData.value.filter(item => !item.children || item.children.length === 0));
const activeIndex = ref('Home');
const router = useRouter();
const handlemenu = (item: MenuItem) => {
router.push(item.index);
};
const handlemenuchild = (item: MenuItem, subItem: MenuItem) => {
router.push(subItem.index);
};
// const isCollapse = ref(true)
const store = useAllDataStore();
const TitleText = computed(() => {
return store.isCollapse ? '平台' : '测试平台管理';
});
const isCollapse = computed(() => store.isCollapse);
/*
// 使用 defineComponent 显式命名组件
export const MainAsideCont = defineComponent({
name: 'MainAsideCont'
});
*/
</script>
<style>
.el-menu {
height: 100%; /* 设置整个布局的高度为 100%,确保布局占满整个视口 */
border-right: none; /* 去掉右边框 */
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 180px;
min-height: 400px;
}
.el-menu-vertical-demo.el-menu--collapse {
width: 60px; /* 收缩时的宽度 */
}
.icon {
margin-right: 8px; /* 图标与文字之间的间距 */
font-size: 18px; /* 图标的大小 */
width:18px;
height:18px;
size:8px;
color: #606266; /* 图标的默认颜色 */
vertical-align: middle; /* 垂直居中对齐 */
}
/* 鼠标悬停时的样式 */
.icon:hover {
color: #409eff; /* 鼠标悬停时图标的颜色 */
}
</style>
四 、运行效果
点击expand
菜单数据来源Mock模拟资料,也就是api资料。
参考文献:
- Axios 官方网址:https://axios-http.com/
- Mockjs官方网址:https://mockjs.com/