本文档从零开始搭建一个通用的管理后台,技术栈为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
我正在使用i18n从头开始构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在rubyonrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi
我安装了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
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道rubyonrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim
是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s
导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/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
我是ruby的新手,我认为重新构建一个我用C#编写的简单聊天程序是个好主意。我正在使用Ruby2.0.0MRI(Matz的Ruby实现)。问题是我想在服务器运行时为简单的服务器命令提供I/O。这是从示例中获取的服务器。我添加了使用gets()获取输入的命令方法。我希望此方法在后台作为线程运行,但该线程正在阻塞另一个线程。require'socket'#Getsocketsfromstdlibserver=TCPServer.open(2000)#Sockettolistenonport2000defcommandsx=1whilex==1exitProgram=gets.chomp
我想用这两种语言中的任何一种(最好是ruby)制作一个窗口管理器。老实说,除了我需要加载某种X模块外,我不知道从哪里开始。因此,如果有人有线索,如果您能指出正确的方向,那就太好了。谢谢 最佳答案 XCB,X的下一代API使用XML格式定义X协议(protocol),并使用脚本生成特定语言绑定(bind)。它在概念上与SWIG类似,只是它描述的不是CAPI,而是X协议(protocol)。目前,C和Python存在绑定(bind)。理论上,Ruby端口只是编写一个从XML协议(protocol)定义语言到Ruby的翻译器的问题。生
这是我在ActiveAdmin中的自定义页面ActiveAdmin.register_page"Settings"doaction_itemdolink_to('Importprojects','settings/importprojects')endcontentdopara"Text"endcontrollerdodefimportprojectssystem"rakedataspider:import_projects_ninja"para"OK"endendend我想做的是,当我单击“导入项目”按钮时,我想在Controller中执行rake任务。但是我无法访问该方法。可能是什