草庐IT

基于element-ui 搭建管理后台

夜跑者 2023-04-10 原文

本文档从零开始搭建一个通用的管理后台,技术栈为vue2.0 + vue-router + vuex + element-ui + axios 。最终效果如下:

 

左侧为菜单栏,右侧包含头部,面包屑,主内容区。左侧菜单栏可折叠,可全屏,刷新页面后还是之前页面状态。gitee地址:

https://gitee.com/liubangbo/yilin-admain/tree/master

如对您有帮助,麻烦给个star。另外如有问题,欢迎在此留言讨论。

下面对此框架主要部分进行详细阐述。

1. 准备工作

     此框架采用了vue-cli 脚手架创建的项目,选中vuex vue-router,然后按官方文档安装element-ui 并按需加载。最后在安装sass sass-loader的时候如果报错的话,则直接在package.json的devDependencies字段加上"sass": "^1.53.0",  "sass-loader": "^8.0.2",  再npm install 一下就可以了。

2. 模块化业务

   业务按模块划分,在每个模块下写上路由,以及vuex状态。通过接口获取动态路由,对动态路由进行了一个map映射,这样前端就可以自己组织页面结构了。这一块代码在src/register-route-store.js 中:

import { router, store } from "./main";
// import { get } from "./http/index.js";  //real routes fetched from platform
import { get } from "./mockRoutes.js";  //mock routes 

export let routesMap = new Map();

const recursionRoutes = (destRoutes, sourceRoutes) => {
  if (sourceRoutes.length) {
    let temp = null;
    sourceRoutes.forEach((item, index) => {
      temp = routesMap.get(item.id) ? routesMap.get(item.id) : {};
      destRoutes.children &&
        destRoutes.children.push(
          Object.assign(
            {
              path: temp.path,
              component:
                `${item.children}` && `${item.children.length}`
                  ? (resolve) =>
                      require([
                        "@/layouts/components/rightRouteView/index.vue",
                      ], resolve)
                  : temp.component,
              children:
                `${item.children}` && `${item.children.length}` ? [] : null,
            },
            {
              meta: {
                label: item.label,
                icon: item.icon,
              },
            }
          )
        );
      if (item.children && item.children.length) {
        recursionRoutes(destRoutes.children[index], item.children);
      }
    });
  }
};

const register_dynamicRoutes_store = (dynamicRoutes) => {
  console.log("dynamic routes: ", dynamicRoutes);
  let routes = [...require("@/layouts/routes/index.js").default];
  store.registerModule("layouts", require("@/layouts/store/index.js").default);

  dynamicRoutes.length &&
    dynamicRoutes.forEach((item, index) => {
      require(`@/views${item.path}/routes/index.js`);
      routes[0].children.push(
        Object.assign(
          {
            path: item.path,
            component: (resolve) =>
              require([
                `${item.children}` && `${item.children.length}`
                  ? "@/layouts/components/rightContent/index.vue"
                  : `@/views${item.path}`,
              ], resolve),
            children:
              `${item.children}` && `${item.children.length}` ? [] : null,
          },
          {
            meta: {
              label: item.label,
              icon: item.icon,
            },
          }
        )
      );
      store.registerModule(
        item.path.slice(1),
        require(`@/views${item.path}/store/index.js`).default
      );
      if (item.children && item.children.length) {
        recursionRoutes(routes[0].children[index], item.children);
      }
    });

  console.log("mapped routes is: ", routes);
  router.addRoutes(routes);
  store.commit("layouts/setSubRoutesList", routes[0].children);
};

const register_login_route = () => {
  const routes = [...require("@/views/login/routes/index.js").default];
  router.addRoutes(routes);
};

export const getRoutes = async () => {
  try {
    const res = await get({
      urlKey: "layouts-getNav",
    });

    if (res.success && res.data && res.data.length) {
      //had login, register dynamic routes
      register_dynamicRoutes_store(res.data);
      router.replace({ path: "/" });
    }
  } catch (error) {
    //no login, register login route
    console.error("get nav error, go to login: ", error);
    register_login_route();
    router.push({
      path: "/login",
    });
  }
};
getRoutes();

3. axios封装

    之前接触的框架,网络接口一般定义在一个文件中,所有业务模块用到的网络接口都写到一个文件中,文件比较长,维护起来也费尽。这里我们把网络接口也进行了业务划分,每个模块写自己用到的网络接口。这部分代码在src/http/index.js文件中:

import axios from "axios";
import { getStorage } from "@/common/js/util.js";
import { TOKEN } from "@/common/js/constant.js";

let pagesUrls = [];
pagesUrls.push({
  key: "layouts-getNav",
  url: "web/user/getNav",
});
const _pagesUrls = require.context("../views/", true, /urls\/index\.js/);

_pagesUrls.keys().forEach((key) => {
  const _moduleName = key.split("/")[1];
  const _moduleUrls = require("@/views/" +
    _moduleName +
    "/urls/index.js").default;
  pagesUrls.push(..._moduleUrls);
});

const urlMap = new Map();
pagesUrls.forEach((i) => {
  if (i.url) {
    urlMap.set(i.key, {
      url: "/" + i.url.replace(/^\//, ""), //  both sg/cms/homepage  and  /sg/cms/homepage  are OK
    });
  }
});

let _httpRequest = (obj, _method) => {
  if (Object.prototype.toString.call(obj) !== "[object Object]") {
    console.error(`the params of http request should be Object`);
    return;
  }
  if (!obj.urlKey || !obj.urlKey.length) {
    console.error(`url is empty, you should set it`);
    return;
  }

  const hostKey = obj.hostKey || "HOST";
  let _url = urlMap.get(obj.urlKey).url;
  if (obj.dynamic) _url = `${_url}${obj.dynamic}`; //dynamic url
  delete obj.urlKey;
  delete obj.hostKey;
  return new Promise((resolve, reject) => {
    let _params = {
      method: _method,
      url: _url,
      baseURL: process.env[`VUE_APP_${hostKey.toUpperCase()}`],
    };
    Object.assign(_params, obj);
    axios(_params)
      .then((res) => {
        resolve(res.data);
      })
      .catch((err) => {
        console.error("axios error: ", err.response);
        if (err.response.data.code === 401) {
          // loginAgain()
        }
        reject(err);
      });
  });
};

axios.interceptors.request.use((config) => {
  const token = getStorage(TOKEN);
  if (token) {
    config.headers["novaAuth"] = token;
  } else {
    config.headers["Authorization"] = "Basic dGVuYW50OjEyMzQ1Ng==";
  }
  return config;
});

axios.interceptors.response.use((res) => {
  console.log("http res: ", res);
  //TODO token过期需要处理
  return res;
});
/**
 * get方法,对应get请求
 * @param {Object} obj
 */
export function get(obj) {
  return _httpRequest(obj, "GET");
}
/**
 * post方法,对应post请求
 * @param {Object} obj
 */
export function post(obj) {
  return _httpRequest(obj, "POST");
}

4. 常量定义

在项目中如果多人协作开发,定义通用常量还是比较重要的,防止出现奇怪bug。例如local-storage的key,我们统一写到常量文件中。这部分代码在src/common/js/constant.js中:

const TOKEN = "iotToken"
const USER_NAME = "userName"

const TABS = "tabs"
const ACTIVE_TAB = "activeTabs"

export {
  TOKEN,
  USER_NAME,
  TABS,
  ACTIVE_TAB
}

5. 假路由数据

    在这个通用框架中我们定义了一个假路由数据,如果你们后端返回的动态路由和这个假路由一样,那么这个框架就可以直接拿来用了,直接上手写业务。假路由数据在src/mockRoutes.js中。

const mockRoutes = {
  code: 200,
  success: true,
  data: [
    {
      id: "8",
      icon: "el-icon-setting",
      label: "系统管理",
      path: "/config",
      children: [
        {
          id: "9",
          icon: "el-icon-setting",
          label: "页面管理",
          path: "/web",
          children: [],
        },
      ],
    },
    {
      id: "100",
      icon: "el-icon-user",
      label: "测试",
      path: "/test",
      children: [
        {
          id: "101",
          icon: "el-icon-user",
          label: "测试一级菜单",
          path: "/test1",
          children: [
            {
              id: "101-0",
              icon: "el-icon-user",
              label: "一级子页面",
              path: "/big",
              children: [],
            },
          ],
        },
        {
          id: "102",
          icon: "el-icon-user",
          label: "测试一级页面",
          path: "/test2",
          children: [],
        },
        {
          id: "103",
          icon: "el-icon-user",
          label: "测试一级菜单",
          path: "/test3",
          children: [
            {
              id: "103-0",
              icon: "el-icon-user",
              label: "测试二级菜单",
              path: "/third",
              children: [
                {
                  id: "103-0-0",
                  icon: "el-icon-user",
                  label: "二级子页面",
                  path: "/small",
                  children: [],
                },
              ],
            },
          ],
        },
      ],
    },
  ],
};
const get = (obj) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (obj) {
        resolve(mockRoutes);
      } else {
        reject({});
      }
    }, 200);
  });
};

export { get };

在src/register-route-store.js中的如下代码是进行真假路由数据切换的地方:

// import { get } from "./http/index.js";  //real routes fetched from platform
import { get } from "./mockRoutes.js";  //mock routes 

有关基于element-ui 搭建管理后台的更多相关文章

  1. ruby - i18n Assets 管理/翻译 UI - 2

    我正在使用i18n从头开始​​构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在ruby​​onrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi

  2. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  3. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  4. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

  5. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  6. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  7. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  8. ruby - 如何在 ruby​​ 中运行后台线程? - 2

    我是ruby​​的新手,我认为重新构建一个我用C#编写的简单聊天程序是个好主意。我正在使用Ruby2.0.0MRI(Matz的Ruby实现)。问题是我想在服务器运行时为简单的服务器命令提供I/O。这是从示例中获取的服务器。我添加了使用gets()获取输入的命令方法。我希望此方法在后台作为线程运行,但该线程正在阻塞另一个线程。require'socket'#Getsocketsfromstdlibserver=TCPServer.open(2000)#Sockettolistenonport2000defcommandsx=1whilex==1exitProgram=gets.chomp

  9. ruby - (Ruby || Python) 窗口管理器 - 2

    我想用这两种语言中的任何一种(最好是ruby​​)制作一个窗口管理器。老实说,除了我需要加载某种X模块外,我不知道从哪里开始。因此,如果有人有线索,如果您能指出正确的方向,那就太好了。谢谢 最佳答案 XCB,X的下一代API使用XML格式定义X协议(protocol),并使用脚本生成特定语言绑定(bind)。它在概念上与SWIG类似,只是它描述的不是CAPI,而是X协议(protocol)。目前,C和Python存在绑定(bind)。理论上,Ruby端口只是编写一个从XML协议(protocol)定义语言到Ruby的翻译器的问题。生

  10. ruby-on-rails - 事件管理员和自定义方法 - 2

    这是我在ActiveAdmin中的自定义页面ActiveAdmin.register_page"Settings"doaction_itemdolink_to('Importprojects','settings/importprojects')endcontentdopara"Text"endcontrollerdodefimportprojectssystem"rakedataspider:import_projects_ninja"para"OK"endendend我想做的是,当我单击“导入项目”按钮时,我想在Controller中执行rake任务。但是我无法访问该方法。可能是什

随机推荐