1. <template>
  2. <div class="home_container">
  3. <div class="home_header">
  4. 头部
  5. </div>
  6. <div class="home_menu">菜单</div>
  7. <div class="home_content">右侧内容</div>
  8. </div>
  9. </template>
  10. <script setup lang='ts'>
  11. </script>
  12. <style lang="less" scoped>
  13. .home_container{
  14. position: relative;
  15. height: 100%;
  16. .home_header{
  17. height: 70px;
  18. background-color: skyblue;
  19. }
  20. .home_menu{
  21. position: absolute;
  22. top: 70px;
  23. left: 0;
  24. bottom: 0;
  25. width: 250px;
  26. background-color: pink;
  27. }
  28. .home_content{
  29. position: absolute;
  30. top: 70px;
  31. left: 250px;
  32. bottom: 0;
  33. width: 1030px;
  34. background-color: yellow;
  35. }
  36. }
  37. </style>

pinia

需要存储接口返回的用户信息,借助pinia
新建store/index.ts

  1. import { defineStore } from "pinia";
  2. import { Names } from "./store-namespace";
  3. export const useUserStore = defineStore(Names.User, {
  4. state: () => {
  5. return {
  6. menus: [],
  7. };
  8. },
  9. //类似于computed 可以帮我们去修饰我们的值
  10. getters: {},
  11. //可以操作异步 和 同步提交state
  12. actions: {
  13. updateMenus(menus: Array<{}>){
  14. this.menus = menus;
  15. }
  16. },
  17. });

方便管理,引入仓库命名空间

  1. export const enum Names {
  2. User = 'USER'
  3. }

在main.ts里挂载

  1. import { createPinia } from 'pinia'
  2. const store = createPinia();
  3. app.use(store);

在组件里引用时

  1. import {useUserStore} from '../../store'
  2. const User = useUserStore();
  3. getAdminInfoApi().then(res => {
  4. if(res.code === 200){
  5. User.menus = res.data.menus;
  6. console.log(User.menus);
  7. router.push('/home')
  8. }
  9. })

数据预处理

目前得到的menus如下
image.png
但是我们使用elementPlus里的菜单组件时,若需要使用二级菜单,必须处理其数据结构
并且,一级菜单的id与其对应的二级菜单的parentId一致
借助getters产生新的数据来渲染菜单
为了让二级菜单更好找到一级菜单,我们把新数据定义为对象,而且让一级菜单的id作为属性名key,一级菜单对象作为值

  1. getters: {
  2. getNewMenus() {
  3. const newMenus:NewMenus = {};
  4. const menus = this.menus;
  5. for (let i = 0; i < menus.length; ++i) {
  6. if (menus[i].parentId === 0) {
  7. newMenus[menus[i].id] = menus[i]
  8. }
  9. }
  10. return newMenus;
  11. },
  12. },

二级菜单需要对号入座,放到其parentId对应的一级菜单的children属性中
注意,因为要给newMenus增加属性,因此在格式定义上也需要优化

  1. //定义menus对象的接口,children只有一级菜单才有,才用?
  2. interface MenuObj {
  3. parentId: number;
  4. id: number;
  5. children?: MenuObj[];
  6. }
  7. //仓库数据的menus格式
  8. interface State {
  9. menus: MenuObj[];
  10. }
  11. // 二级菜单的menus
  12. interface NewMenus {
  13. [key: number]: MenuObj;
  14. }
  1. getters: {
  2. getNewMenus(state) {
  3. const newMenus: NewMenus = {};
  4. const menus = state.menus;
  5. for (let i = 0; i < menus.length; ++i) {
  6. if (menus[i].parentId === 0) {
  7. newMenus[menus[i].id] = { ...menus[i] };
  8. }
  9. }
  10. for (let i = 0; i < menus.length; ++i) {
  11. if (menus[i].parentId !== 0) {
  12. let parentId = menus[i].parentId;
  13. newMenus[parentId] = newMenus[parentId] || {};
  14. newMenus[parentId].children = newMenus[parentId].children || [];
  15. newMenus[parentId].children?.push(menus[i]);
  16. }
  17. }
  18. return newMenus;
  19. },
  20. },

因为每个二级菜单对象里只有基本数据类型,使用浅拷贝即可
newMenus[menus[i].id] = { …menus[i] };

使用两个for循环:
避免有些一级菜单还没加到数组中,其二级菜单就已经被查找到,从而失去该有的位置。

确保newMenus[parentId]有值,避免遇到undefined
newMenus[parentId] = newMenus[parentId] || {};
因为需要添加children属性,为了代码兼容性,给一个空数组作为默认值
newMenus[parentId].children = newMenus[parentId].children || [];

pinia持久化

yarn add pinia-plugin-persist

  1. import { createPinia } from 'pinia'
  2. import piniaPersist from 'pinia-plugin-persist'
  3. const app = createApp(App);
  4. const store = createPinia();
  5. store.use(piniaPersist);
  6. app.use(store); //记得调整store与路由的挂载顺序

最后在仓库中需要持久化存储的数据里使用

  1. persist: {
  2. //这里存储默认使用的是session
  3. enabled: true,
  4. strategies: [
  5. {
  6. //更改默认存储,我更改为localStorage
  7. storage: localStorage,
  8. // 可以选择哪些进入local存储,这样就不用全部都进去存储了
  9. paths: ["menus"],
  10. },
  11. ],
  12. },