vue2实现简易后台管理系统

1,629 阅读5分钟

前言

一眨眼就是100天, 距离上次更次文章已经是三个月前的事了😂,惰性大于自觉了这三个月实在是颓废,再加上工作上稍微有些忙,就把是不是更新文章的习惯给搁置掉了,虽然文笔技术都不咋地,最起码习惯还是好的,在不动动我恐怕是要废了,刚好今天有空把之前完成一大半的东西,删删改改的整理出来,给大家分享一下;

这篇文章其实就是我之前后端项目的前端管理系统,如果大家有兴趣可以去看看 koa+mysql 项目实战 前端项目地址在这里 👉 点我跳转

为什么我不去实现客户端系统而去实现管理后台呢?😥其实有些事情并不是你想咋样就咋样的,开始的时候我以为会了Node 会了 Vue 我就可以自己写项目了(求轻喷),不用再像刚学习前端的时候再某宝,某鱼看到的课一样,没有后端地址全是自己 mock,其实不然,自己做项目最大的难度是造数据,数据从何来,况且客户端对数据的要求还是比较高的,要想做出来看的过去并不是随随便便弄点就可以了,我没有那个耐心也没有那个精力 so 就先做了后台管理。

项目预览

说了这么久嘴都干了,先给掘友们看看这个管理系统大致长什么样子

image.png

项目结构

image.png

目录结构是仿 vue-element-admin 设计的,不得不说大佬就是大佬很强,大部分文件夹应该一眼就能看出来是用来放什么的,下面就列举一些不好确认和分辨的

  • libs:放了一些工具方法,请求二次封装的
  • layout:除去中心的表格区域的其他区域内容
  • plugins:目前里面就是按需引入的 element 后续可以放一些其他三方插件

项目重点

开始重要环节,半斤教八两😂,我敢讲看你敢听不?

国际化

Vue 项目的国际化主要是靠 i18n 这个插件 👉 这里 ,大家可以把代码 clone 下来看一下 local 文件夹;我这边的做法是把中英文分别维护然后在一个文件里面整合,最后放到 main.js 中去注册这个插件;

// 目录:local/lang/en-US.js
export default {
  button: "button"
}
// 目录:local/lang/zh-CN.js
export default {
  button: "按钮"
}
// local/index.js
// 先把插件导入进来
import Vue from "vue";
import VueI18n from "vue-i18n";
// 国际化内容导入进来
import customZhCn from "./lang/zh-CN";
import customUsEn from "./lang/en-US";
// element 按需加载就要这样搞,官网有写
import enLocale from "element-ui/lib/locale/lang/en";
import zhLocale from "element-ui/lib/locale/lang/zh-CN";
import ElementLocale from "element-ui/lib/locale";

Vue.use(VueI18n);
// 这里是获取浏览器的语言
const navLang = navigator.language;
// 如果有就用浏览器的没有就是一个 false
const localLang = navLang === "zh-CN" || navLang === "en-US" ? navLang : false;
let lang = localLang ? lang = localLang : "zh-CN";
// 把语言对象都合并一下
const messages = {
  "zh-CN": Object.assign(zhLocale, customZhCn),
  "en-US": Object.assign(enLocale, customUsEn)
};
const i18n = new VueI18n({
  locale: lang,
  messages
});
// element 官网操作
ElementLocale.i18n((key, value) => i18n.t(key, value));
// 暴露出去最后在main.js中去注册
export default i18n;

在使用的时候,比如我们要使用 button 在模板中就是 {{ $t('button') }} 在脚本中就是 this.$t('button')

递归侧边栏

/layout/Sidebar/index.vue 中把 路由信息传递到 sidenar-item 组件中

<sildebar-item
  v-for="route in routes"
  :key="route.path"
  :item="route"
  :base-path="route.path"
></sildebar-item>

用到递归的代码在 /layout/Sidebar/SidebarItem.vue 中;这里我把代码 copy 出来一份,去掉与递归无效的加上注释讲解;

<template>
  <!-- hide其实是路由信息用来判断这个是否显示 -->
  <div v-if="!item.hide">
    <!-- 这里乍一看可能比较晦涩没关系,咱们先看方法 methods
          1, 有children并且没有hide的时候 返回 true
          2, showingChildren的length为0的时候 true
          3,  以上两种情况的前提都是 isHasOneChild 会返回 true
          4, 一直循环到没有children了就进去else阶段
    -->
    <template
      v-if="
        isHasOneChild(item.children, item) &&
          (!onlyOneChild.children || onlyOneChild.noShowingChildren)
      "
    > 
      <!-- 条件成立一直执行这里的代码 -->
      <el-menu-item :index="resolvePath(onlyOneChild.path)"></el-menu-item>
    </template>
    <el-submenu v-else :index="resolvePath(item.path)">
      <!-- 本质就是自己调用自己,但是要记得组件必须有name属性 
           5, else 这里呢又是调用了自己,并且是一个循环,所以就会一直递归的调用循环数据以及children,知道全部走一遍为空的时候,终止了
      -->
      <sidebar-item
        v-for="child in item.children"
        :key="child.path"
        :item="child"
        :base-path="resolvePath(child.path)"
      />
    </el-submenu>
  </div>
</template>
<script>
import { isExternal } from "@/libs/util";

export default {
  name: "SidebarItem",
  props: {
    // route object
    item: {
      type: Object,
      required: true
    },
    basePath: {
      type: String,
      default: ""
    }
  },
  data() {
    return {
      onlyOneChild: {}
    };
  },
  methods: {
    // 接口子路由数组和路由item
    isHasOneChild(children = [], route) {
      const showingChildren = children.filter(item => {
        if (item.hide) {
          return false;
        } else {
          this.onlyOneChild = item;
          return true;
        }
      });

      // 如果 children 只有一个 默认显示 children
      if (showingChildren.length === 1) {
        return true;
      }
      // 如果没有 children 显示自身
      if (showingChildren.length === 0) {
        this.onlyOneChild = { ...route, path: "", noShowingChildren: true };
        return true;
      }

      return false;
      // 总结:
      // 1, 子路由(children)有 hide 返回 fasle
      // 2, 子路由(children)没有 hide 返回 true 并且把 路由信息赋值给 onlyOneChild
      // 3, showingChildren的length为1 是 true
      // 4,showingChildren的length为0 是 true 但是给 onlyOneChild 又赋值了
    },
    resolvePath(routePath) {
      if (isExternal(routePath)) {
        return routePath;
      }
      if (isExternal(this.basePath)) {
        return this.basePath;
      }
      return path.resolve(this.basePath, routePath);
    }
  }
};

elementTable 二次封装

table的二次封装主要实现,就是接收一个对象,在初始化的时候,给 table 把基本的默认值设置上 ,如果外部传递过来的参数有变化,就用外部的,并且把 pagination 组件所需要的参数也和 table 进行合并封装,这样下次用的时候,就默认会带分页功能了,组件内部暴露一些方法,在传递数据,或者数据更改之后调用此方法更新 table 内容;大家可以看一下代码,个人感觉注释很详细的😂 /components/proTable/index.vue /components/proTable/Pagination.vue

项目启动

由于这个项目是个纯前端项目没有 mock 所以掘友们如果想启动恐怕,还要去把我那个后端项目一块 clone 下来,强烈建议先启动后端,自己随意创建几条数据,然后再启动前端;

前端启动的时候注意修改一下 vue.config.js 文件中的代理地址;然后执行前端项目启动两件套 npm install npm run serve

最后感觉掘友观看,并自觉点赞 手动滑稽 完结撒花!!!