十九.如何动态添加后端返回的菜单实现动态路由的添加呢?
1. router/index.js 文件
import { createRouter, createWebHashHistory } from "vue-router";
import Admin from "~/Layout/admin.vue";
import Index from "~/pages/index.vue";
import Login from "~/pages/Login/login.vue";
import NotFound from "~/pages/NotFound/404.vue";
import GoodList from "~/pages/Goods/List.vue";
import CategoryList from "~/pages/Category/ListCategory.vue";
// const routes = [
// {
// path: "/",
// component: Admin,
// children: [
// {
// path: '/',
// component: Index,
// meta: {
// title: "后台首页"
// }
// },
// {
// path: '/goods/list',
// component: GoodList,
// meta: {
// title: "商品管理"
// }
// },
// {
// path: '/category/list',
// component: CategoryList,
// meta: {
// title: "分类管理"
// }
// }
// ]
// },
// {
// path: "/login",
// component: Login,
// meta: {
// title: "登录"
// }
// },
// {
// path: "/:pathMatch(.*)*",
// name: "NotFound",
// component: NotFound,
// meta: {
// title: "404-NotFound"
// }
// },
// ];
// 这个是公共路由 所有用户共享
const routes = [
{
path: "/",
name: "admin",
component: Admin,
},
{
path: "/login",
name: "login",
component: Login,
meta: {
title: "登录",
},
},
{
path: "/:pathMatch(.*)*",
name: "NotFound",
component: NotFound,
meta: {
title: "404-NotFound",
},
},
];
// 1. 动态路由,用于匹配菜单动态添加路由
const aysncRoutes = [
{
path: "/",
component: Index,
name: "/",
meta: {
title: "后台首页",
},
},
{
path: "/goods/list",
component: GoodList,
name: "/goods/list",
meta: {
title: "商品管理",
},
},
{
path: "/category/list",
component: CategoryList,
name: "/category/list",
meta: {
title: "分类管理",
},
},
];
export const router = createRouter({
history: createWebHashHistory(),
routes,
});
// 2. 动态添加路由的方法
export const addRoutes = (menus) => {
console.log(menus);
// 是否有新的路由
let hasNewRoutes = false;
const findAndAddRoutesByMenus = (arr) => {
arr.forEach((e) => {
let item = aysncRoutes.find((o) => o.path == e.frontpath);
// item: 是否有item; router.hasRoute():检查路由是否存在
// router.hasRoute(): 传递一个name,所以要在路由中写上name名称
if (item && !router.hasRoute(item.path)) { // !router.hasRoute(item.path): 没有注册过路由
// addRoute():参数一: 父级路由的name值,参数二:满足条件的对象
router.addRoute("admin", item);
hasNewRoutes = true;
}
// e: 是一个对象
if (e.child && e.child.length > 0) {
findAndAddRoutesByMenus(e.child);
}
});
};
findAndAddRoutesByMenus(menus);
return hasNewRoutes;
// console.log(router.getRoutes());
};
2.
// 专门处理权限相关的代码
// 1. 引入路由实例
import { router, addRoutes } from "~/router";
import { getToken } from "~/commonCookie/Auth";
import { Toast, showFullLoading, hideFullLoading } from "~/commonCookie/utils";
import store from "./store";
// 前置路由守卫
router.beforeEach(async (to, from, next) => {
// 只要路由发生了变化 要实现loading效果
showFullLoading();
const token = getToken();
if (!token && to.path !== "/login") {
Toast("请先登录", "error");
return next({ path: "/login" });
}
// 防止重复登录
if (token && to.path == "/login") {
Toast("请务重复登录", "error");
return next({ path: from.path ? from.path : "/" });
}
let hasNewRoutes = false;
// 如果用户登录了 ,自动获取用户信息, 并储存在vuex中,
// 这种是为了防止 第一次可以获取到数据, 但是在页面刷新以后数据会丢失
if (token) {
const res = await store.dispatch("getInfo");
// console.log(res);
// 动态添加路由 hasNewRoutes:返回值是Boolean当有新的路由的时候 值为true
hasNewRoutes = addRoutes(res.menus);
}
// 设置页面标题
// console.log(to.meta.title); // 显示的是当前路由的title
let title = to.meta?.title + "--振楚后台系统";
document.title = title;
console.log(to);
// 防止路由刷新页面消失,hasNewRoutes:返回值是Boolean当有新的路由的时候 值为true
hasNewRoutes ? next(to.fullPath) : next();
});
// 全局后置守卫
router.afterEach((to, from) => {
// 当路由加载完成以后 关闭全局的进度条
hideFullLoading();
});
二十. 封装一个完整版的标签导航
1. FTagListHooks.js
import { ref } from "vue";
import { useRoute, onBeforeRouteUpdate, useRouter } from "vue-router";
import { useCookies } from "@vueuse/integrations/useCookies";
export const useTabList = () => {
const route = useRoute();
const router = useRouter();
const cookie = useCookies();
const activeTab = ref(route.path);
const tabList = ref([
{
title: "后台首页",
path: "/",
},
]);
const changeTab = (t) => {
console.log(t);
activeTab.value = t;
router.push(t);
};
const removeTab = (t) => {
console.log(t); // 拿到path值
let tabs = tabList.value;
let a = activeTab.value;
if (a == t) {
tabs.forEach((tab, index) => {
if (tab.path == t) {
const nextTab = tabs[index + 1] || tabs[index - 1];
if (nextTab) {
a = nextTab.path; // 点击删除,匹配到当前上一个或者写一个的路径
}
}
});
}
activeTab.value = a;
tabList.value = tabList.value.filter((tab) => tab.path != t);
cookie.set("tabList", tabList.value);
};
// 监听当前路由改变
onBeforeRouteUpdate((to, from) => {
// console.log(to);
// console.log(from);
activeTab.value = to.path;
addTab({
title: to.meta.title,
path: to.path,
});
});
const addTab = (tabObj) => {
let noTab = tabList.value.findIndex((t) => t.path == tabObj.path) == -1; // 代表此时循环是数据是没有这个tab
if (noTab) {
tabList.value.push(tabObj);
}
cookie.set("tabList", tabList.value);
};
// 初始化标签导航列表,当你在刷新浏览器的时候 添加的tab标签没有了
const initTabList = () => {
let tabs = cookie.get("tabList");
if (tabs) {
tabList.value = tabs;
}
};
initTabList();
// 关闭标签
const handleClode = (c) => {
// closeOther
// closeAll
if (c == "closeAll") {
// 切换到首页
activeTab.value = "/";
// 过滤只剩下首页
tabList.value = [
{
title: "后台首页",
path: "/",
},
];
} else if (c == "closeOther") {
tabList.value = tabList.value.filter(
(tab) => tab.path == "/" || tab.path == activeTab.value
);
}
cookie.set("tabList", tabList.value);
};
return {
activeTab,
tabList,
changeTab,
removeTab,
handleClode
}
};
2. FTagList.vue
实际截图
二十一. 利用keep-alive 实现 全局 页面缓存
二十二.设置全局过渡动画
二十三.使用 animate.style 库来做动画效果
使用 animate.style 库来做动画效果
二十四.如何实现数字滚动 动画效果
gsap库的地址
1. 要是用一个第三方的gsap库安装命令: npm i gsap
2. 封装一个数字滚动组件 CountTo.vue组件
{{ d.num.toFixed(0) }}
3. 在页面使用
// 3.1 引入
import CountTo from "~/components/CountTo/CountTo.vue"
// 3.2 标签上使用
二十五.对Echarts图标封装
1. 安装Eacharts: cnpm install echarts --save
2. 封装一个Eacharts.vue文件
订单统计
{{ item.text }}
3. 在需要引用的页面中使用
import CheckEChart from "~/components/Compone/ComponentIndexEacharts.vue";