第八章 整理前端代码
前言
这章主要是清除前端多余的代码,移除不用的模块和包,完善用户管理,角色管理,菜单管理,机构管理!
一、清除多余的模块和代码
1、所有跟 mock 相关的不需要 2、跟国际化相关的不需要(用的上国际化的可以留着) 3、多余的路由配置不需要 4、其他用不上的组件库
二、Vue 请求库
我用的是vue-request,不了解的可以先学习下,后面用到地方很多,像接口请求、手动触发请求、loading、分页、刷新、轮询等,都可以用到。
三、分页配置
用法可以参考 vue-request 的官方文档。
ts
import { usePagination } from "vue-request";
分页的默认配置我们自己写一个 pagination.ts,放到 util 目录下
ts
import { PaginationOptions } from "vue-request";
/**
* 后台的默认分页查询信息
*/
const defaultPageOptions: PaginationOptions<any, any> = {
// 默认大小改成20
// @ts-ignore
defaultParams: [{ current: 1, size: 10 }] as any,
// 默认 10毫秒
loadingDelay: 10,
// 默认有个10毫秒的判定 阻止连续触发问题 拖到最后
debounceInterval: 10,
pagination: {
/**
* 后端的分页数据格式
*/
// 当前页面
currentKey: "current",
// 分页大小的key
pageSizeKey: "size",
// 所有页面
totalPageKey: "pages",
// 总条数
totalKey: "total",
},
};
export default defaultPageOptions;
四、显隐配置
对应模态框的显示和隐藏我们也需要一个公共的方法来统一处理,这个钩子方法我放到 hooks 目录下。 visible.ts
ts
/**
* 定义公用模态框的类型信息
* 方便对模态框弹出等处理
*
*/
import { computed, watch } from "vue";
/**
* 显示隐藏对应的props
* 和类型信息
*/
export interface Visible {
visible: boolean;
}
/**
* 相关的emits对应类型
* 参数等定义
*/
export interface VisibleEmits {
(e: "update:visible", visible: boolean): void;
}
/**
* composable 对应的显示隐藏属性
* @param props 属性
* @param emits 事件
* @returns
*/
export const useVisible = (props: Visible, emits: VisibleEmits) => {
// 间接托管的可见状态
const formVisible = computed({
get: () => props.visible,
set: (v) => emits("update:visible", v),
});
return {
formVisible,
/**
* 当状态变为打开的时候
* @param callback 回调执行
* @returns
*/
open: (callback: () => any) => watch(formVisible, (v) => v && callback()),
/**
* 关闭方法
*/
close: () => {
formVisible.value = false;
},
};
};
五、动态加载菜单
官方文档都是把路由菜单写死到文件里面的,但是实际项目中都是从后台动态加载的,我们修改一下,改成从后端读取菜单数据。 代码逻辑也很简单,就是在用户登录完成后请求菜单数据,然后组装菜单,添加到路由就可以了,核心代码如下: MenuStore
ts
import { defineStore } from "pinia";
import { getMenuList } from "@/api/upms/menu";
import { RouteRecordRaw } from "vue-router";
import { MenuState, Menu } from "./types";
const components = import.meta.glob("@/views/**/index.vue");
const modules = Object.fromEntries(
Object.entries(components).map(([k, v]) => [k.slice(5, -4), v])
);
const useMenuStore = defineStore("menu", {
state: (): MenuState => ({
serverMenu: [],
}),
getters: {
asyncMenus(state: MenuState): RouteRecordRaw[] {
return state.serverMenu as RouteRecordRaw[];
},
},
actions: {
generatorDynamicRouter(menuList: Menu[], parent: string): RouteRecordRaw[] {
return menuList?.map((item) => {
const { component, name, icon, path, sort, title } = item;
// ??为NULL判断运算符,运算符左侧的值为null或undefined时,才会返回右侧的值
const children = item.children ?? [];
const meta = {
icon,
title,
sort,
};
const isChild = children != null && children.length !== 0;
const currentRouter: RouteRecordRaw = {
// 路由地址动态拼接生成如 /dashboard/workplace
path: parent ? `${parent}/${path}` : `${path || ""}`,
name,
meta,
children: [],
// 动态导入页面
component:
!component || component === "Layout"
? () => import("@/layout/default-layout.vue")
: modules?.[component],
};
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
if (!currentRouter.path.startsWith("http")) {
currentRouter.path = currentRouter.path.replace("//", "/");
}
// 是否有子菜单,并递归处理
if (isChild) {
currentRouter.children = this.generatorDynamicRouter(
children,
currentRouter.path
);
}
return currentRouter;
});
},
async fetchServerMenuConfig() {
const data = await getMenuList();
this.serverMenu = this.generatorDynamicRouter(data, "");
},
clearServerMenu() {
this.serverMenu = [];
},
},
});
export default useMenuStore;
路由守卫:
六、管理页面
管理页面就很简单,参考官方的文档写起来就没啥难度,这里我就不再啰嗦了。前端页面大部分都是相通的,写好一个其他的都可以复用,看看我效果:
当前版本 tag:1.0.7 代码仓库
七、 体验地址
后台数据库只给了部分权限,报错属于正常!
想学的老铁给点点关注吧!!!
不清楚明白的地方欢迎留言,空了我会给大家解答的!!!
我是阿咕噜,一个从互联网慢慢上岸的程序员,如果喜欢我的文章,记得帮忙点个赞哟,谢谢!