Skip to content

第八章 整理前端代码

前言

这章主要是清除前端多余的代码,移除不用的模块和包,完善用户管理,角色管理,菜单管理,机构管理!

一、清除多余的模块和代码

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 代码仓库

七、 体验地址

后台数据库只给了部分权限,报错属于正常!

想学的老铁给点点关注吧!!!

不清楚明白的地方欢迎留言,空了我会给大家解答的!!!

我是阿咕噜,一个从互联网慢慢上岸的程序员,如果喜欢我的文章,记得帮忙点个赞哟,谢谢!