Commit 556cb3d8 authored by trumansdo's avatar trumansdo
Browse files

vue最新版实现完全的异步加载组件

parent fb33a03c
# --------自定义应用属性
app:
name: SpringBoot-Plus
#打开审计功能,开发模式应该关闭
audit:
enable: false
# 1,1 是管理员密码,相当于启动后就登录,方便测试,系统需要取消这俩个配置
user:
id: 1
orgId: 1
# 文件操作的根目录配置 请根据各自计算机配置
localFile:
root: E:\code_workspace\temp_space\
# --------beetl配置
beetl:
suffix: html
# -------beetlsql配置
beetlsql:
ds:
baseDataSource:
basePackage: com.ibeetl.admin
daoSuffix: Dao
dbStyle: org.beetl.sql.core.db.MySqlStyle
mutiple:
datasource: baseDataSource
# -------- 文件预览配置
#文档预览服务的调用地址,参考https://gitee.com/kekingcn/file-online-preview 安装,没有如下配置无法使用预览功能
file:
previewURL: http://localhost:8012/onlinePreview
# ---------日志配置
logging:
level:
org:
springframework:
boot:
autoconfigure: ERROR
web:
servlet:
mvc:
method:
annotation: warn
root: info
pattern:
console: '%-4relative [%thread] %-5level %logger{256} %M %L - %msg%n'
# --------服务器undertow配置
server:
port: 8080
undertow:
accesslog:
enabled: true
buffer-size: 4096
direct-buffers: true
io-threads: 4
worker-threads: 20
spring:
cache:
type: caffeine
# 数据源配置
datasource:
baseDataSource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123456
url: jdbc:mysql://127.0.0.1:3306/starter_vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&useInformationSchema=true
username: root
session:
store-type: none
...@@ -5,11 +5,11 @@ import cn.hutool.core.collection.CollUtil; ...@@ -5,11 +5,11 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import java.util.Comparator; import lombok.Data;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import lombok.Data;
/** /**
* 对应前端页面的routes路由表。具体格式如下: { "path": "/profile", "name": "router-name", "meta": { "title": * 对应前端页面的routes路由表。具体格式如下: { "path": "/profile", "name": "router-name", "meta": { "title":
...@@ -20,60 +20,77 @@ import lombok.Data; ...@@ -20,60 +20,77 @@ import lombok.Data;
@Data @Data
public class CoreRoute extends BaseEntity implements Comparable<CoreRoute> { public class CoreRoute extends BaseEntity implements Comparable<CoreRoute> {
private Long id; private Long id;
private Long parentId; private Long parentId;
/** 路由路径,完全参照vue router规范 */ /**
private String path; * 路由路径,完全参照vue router规范
*/
private String path;
/** 路由名称,请确保唯一性 */ /**
private String name; * 路由名称,请确保唯一性
*/
private String name;
/** 路由顺序 */ /**
private Long seq = -999L; * 路由组件component
*/
private String component;
/** 路由元数据信息 */ /**
private CoreRouteMeta meta; * 路由顺序
*/
private Long seq = -999L;
/** 子路由项 */ /**
private List<CoreRoute> children = CollUtil.newArrayList(); * 路由元数据信息
*/
private CoreRouteMeta meta;
public CoreRouteMeta getMeta() { /**
if (ObjectUtil.isNotEmpty(this.meta)) return this.meta; * 子路由项
Map<String, Object> metaMap = MapUtil.builder(getTails()).build(); */
this.meta = BeanUtil.mapToBean(metaMap, CoreRouteMeta.class, true); private List<CoreRoute> children = CollUtil.newArrayList();
return this.meta;
}
@Override public CoreRouteMeta getMeta() {
public boolean equals(Object o) { if (ObjectUtil.isNotEmpty(this.meta)) {
if (o != null && o instanceof CoreRoute) { return this.meta;
CoreRoute othRoute = (CoreRoute) o; }
if (this.getId().equals(othRoute.getId())) { Map<String, Object> metaMap = MapUtil.builder(getTails()).build();
return true; this.meta = BeanUtil.mapToBean(metaMap, CoreRouteMeta.class, true);
} return this.meta;
if (this.getParentId().equals(othRoute.getParentId())) {
return StrUtil.equals(this.getPath(), othRoute.getPath())
&& StrUtil.equals(this.getName(), othRoute.getName());
} else {
return false;
}
} else {
return false;
} }
}
@Override @Override
public int hashCode() { public boolean equals(Object o) {
return Objects.hash(id, parentId, path, name); if (o != null && o instanceof CoreRoute) {
} CoreRoute othRoute = (CoreRoute) o;
if (this.getId().equals(othRoute.getId())) {
return true;
}
if (this.getParentId().equals(othRoute.getParentId())) {
return StrUtil.equals(this.getPath(), othRoute.getPath())
&& StrUtil.equals(this.getName(), othRoute.getName());
} else {
return false;
}
} else {
return false;
}
}
@Override @Override
public int compareTo(CoreRoute o) { public int hashCode() {
if (null == o) return -1; return Objects.hash(id, parentId, path, name);
Long seq1 = this.getSeq() == null ? -999L : this.getSeq(); }
Long seq2 = o.getSeq() == null ? -999L : o.getSeq();
return seq1.compareTo(seq2); @Override
} public int compareTo(CoreRoute o) {
if (null == o) return -1;
Long seq1 = this.getSeq() == null ? -999L : this.getSeq();
Long seq2 = o.getSeq() == null ? -999L : o.getSeq();
return seq1.compareTo(seq2);
}
} }
package com.ibeetl.admin.core.entity; package com.ibeetl.admin.core.entity;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import java.util.List;
import lombok.Data; import lombok.Data;
import java.util.List;
@Data @Data
public class CoreRouteMeta { public class CoreRouteMeta {
/** 路由展示在菜单中显示的标题 */ /**
private String title; * 路由展示在菜单中显示的标题
*/
private String title;
/** 在菜单中显示的图标 */ /**
private String icon; * 在菜单中显示的图标
*/
private String icon;
/** 当前路由可以访问的角色 */ /**
private List<Long> roles = CollUtil.<Long>newArrayList(); * 当前路由可以访问的角色
*/
private List<Long> roles = CollUtil.<Long>newArrayList();
} }
...@@ -66,7 +66,7 @@ public class RoleRoutesElService { ...@@ -66,7 +66,7 @@ public class RoleRoutesElService {
* */ * */
public List<CoreRoute> getRoutes() { public List<CoreRoute> getRoutes() {
List<CoreRoute> routesList = coreFunctionDao.getAllRoutes(); List<CoreRoute> routesList = coreFunctionDao.getAllRoutes();
Map<Long, List<Long>> roleIdList = /*Map<Long, List<Long>> roleIdList =
routesList.stream() routesList.stream()
.collect( .collect(
Collectors.groupingBy( Collectors.groupingBy(
...@@ -80,7 +80,7 @@ public class RoleRoutesElService { ...@@ -80,7 +80,7 @@ public class RoleRoutesElService {
.getMeta() .getMeta()
.getRoles() .getRoles()
.addAll(CollUtil.<Long>removeNull(roleIdList.get(coreRoute.getId()))); .addAll(CollUtil.<Long>removeNull(roleIdList.get(coreRoute.getId())));
} }*/
CoreRoute root = new CoreRoute(); CoreRoute root = new CoreRoute();
root.setId(0L); root.setId(0L);
buildRoutesTree(root, routesList); buildRoutesTree(root, routesList);
...@@ -95,7 +95,9 @@ public class RoleRoutesElService { ...@@ -95,7 +95,9 @@ public class RoleRoutesElService {
* @return * @return
*/ */
private void buildRoutesTree(CoreRoute root, @NotNull List<CoreRoute> allRoutes) { private void buildRoutesTree(CoreRoute root, @NotNull List<CoreRoute> allRoutes) {
if (CollUtil.isEmpty(allRoutes)) return; if (CollUtil.isEmpty(allRoutes)) {
return;
}
List<CoreRoute> childRoutes = List<CoreRoute> childRoutes =
allRoutes.stream() allRoutes.stream()
.filter(route -> route.getParentId().equals(root.getId())) .filter(route -> route.getParentId().equals(root.getId()))
......
getAllRoutes getAllRoutes
=== ===
```sql ```sql
select menu.id, select menu.id,
menu.PARENT_MENU_ID PARENT_ID, menu.PARENT_MENU_ID parent_id,
menu.NAME title, menu.NAME title,
menu.CODE name, menu.CODE name,
menu.ICON, menu.ICON,
ifnull(menu.SEQ, 999999) seq, ifnull(menu.SEQ, 999999) seq,
func.ACCESS_URL path, func.ACCESS_URL path,
func.COMPONENT component,
role_menu.ROLE_ID role_menu.ROLE_ID
from core_menu menu from core_menu menu
left join core_function func on func.ID = menu.FUNCTION_ID left join core_function func on func.ID = menu.FUNCTION_ID
...@@ -28,6 +28,7 @@ RouteMapping ...@@ -28,6 +28,7 @@ RouteMapping
"resultType": "com.ibeetl.admin.core.entity.CoreRoute", "resultType": "com.ibeetl.admin.core.entity.CoreRoute",
"path": "path", "path": "path",
"name": "name", "name": "name",
"component": "component",
"id": "id", "id": "id",
"parentId": "parent_id", "parentId": "parent_id",
"seq": "seq", "seq": "seq",
......
/* /*
* @Author: 一日看尽长安花 * @Author: 一日看尽长安花
* @since: 2019-09-04 20:55:14 * @since: 2019-09-04 20:55:14
* @LastEditTime: 2019-10-29 23:17:53 * @LastEditTime: 2020-05-07 14:39:43
* @LastEditors: 一日看尽长安花 * @LastEditors: 一日看尽长安花
* @Description: * @Description:
*/ */
...@@ -96,7 +96,18 @@ module.exports = { ...@@ -96,7 +96,18 @@ module.exports = {
// 关闭检测函数名称和调用它的左括号之间的空格 // 关闭检测函数名称和调用它的左括号之间的空格
// 'func-call-spacing': 'off', // 'func-call-spacing': 'off',
// 缩进为2个空格 // 缩进为2个空格
indent: ['error', 2], indent: [
'error',
2,
{
ignoredNodes: [
'TemplateLiteral'
]
}
],
'template-curly-spacing': [
'off'
],
// 关闭检测未使用的变量 // 关闭检测未使用的变量
'no-unused-vars': 'off', 'no-unused-vars': 'off',
// 对象展开时总是要添加逗号,一行时行末不需要逗号 // 对象展开时总是要添加逗号,一行时行末不需要逗号
......
...@@ -81,6 +81,8 @@ ...@@ -81,6 +81,8 @@
"devDependencies": { "devDependencies": {
"@babel/core": "7.0.0", "@babel/core": "7.0.0",
"@babel/register": "7.0.0", "@babel/register": "7.0.0",
"@babel/traverse": "^7.9.6",
"@babel/types": "^7.9.6",
"@vue/cli-plugin-babel": "4.2.3", "@vue/cli-plugin-babel": "4.2.3",
"@vue/cli-plugin-eslint": "^4.2.3", "@vue/cli-plugin-eslint": "^4.2.3",
"@vue/cli-plugin-unit-jest": "4.2.3", "@vue/cli-plugin-unit-jest": "4.2.3",
......
/*
* @Author: 一日看尽长安花
* @since: 2019-12-01 11:03:53
* @LastEditTime: 2020-05-07 14:46:53
* @LastEditors: 一日看尽长安花
* @Description:
*/
/** /**
* 点击波纹效果 * 点击波纹效果
* *
...@@ -6,34 +13,48 @@ ...@@ -6,34 +13,48 @@
* @return {[bollean]} [description] * @return {[bollean]} [description]
*/ */
export default function(e, arg_opts) { export default function(e, arg_opts) {
var opts = Object.assign({ var opts = Object.assign(
ele: e.target, // 波纹作用元素 {
type: 'hit', // hit点击位置扩散center中心点扩展 ele: e.target, // 波纹作用元素
bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 type: 'hit', // hit点击位置扩散center中心点扩展
}, arg_opts) bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
var target = opts.ele },
arg_opts
);
var target = opts.ele;
if (target) { if (target) {
var rect = target.getBoundingClientRect() var rect = target.getBoundingClientRect();
var ripple = target.querySelector('.e-ripple') var ripple = target.querySelector('.e-ripple');
if (!ripple) { if (!ripple) {
ripple = document.createElement('span') ripple = document.createElement('span');
ripple.className = 'e-ripple' ripple.className = 'e-ripple';
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' ripple.style.height = ripple.style.width =
target.appendChild(ripple) Math.max(rect.width, rect.height) + 'px';
target.appendChild(ripple);
} else { } else {
ripple.className = 'e-ripple' ripple.className = 'e-ripple';
} }
switch (opts.type) { switch (opts.type) {
case 'center': case 'center':
ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px' ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px';
ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px' ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px';
break break;
default: default:
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px' ripple.style.top =
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px' e.pageY -
rect.top -
ripple.offsetHeight / 2 -
document.body.scrollTop +
'px';
ripple.style.left =
e.pageX -
rect.left -
ripple.offsetWidth / 2 -
document.body.scrollLeft +
'px';
} }
ripple.style.backgroundColor = opts.bgc ripple.style.backgroundColor = opts.bgc;
ripple.className = 'e-ripple z-active' ripple.className = 'e-ripple z-active';
return false return false;
} }
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Description: In User Settings Edit * @Description: In User Settings Edit
* @Author: your name * @Author: your name
* @Date: 2019-09-09 12:16:28 * @Date: 2019-09-09 12:16:28
* @LastEditTime: 2019-10-30 20:32:19 * @LastEditTime: 2020-05-07 15:03:38
* @LastEditors: 一日看尽长安花 * @LastEditors: 一日看尽长安花
*/ */
import { constantRoutes } from '@/router'; import { constantRoutes } from '@/router';
...@@ -11,6 +11,8 @@ import { default as asyncRoutesMap } from '@/router/maps/index'; ...@@ -11,6 +11,8 @@ import { default as asyncRoutesMap } from '@/router/maps/index';
import { deepClone, objectMerge } from '@/utils/index'; import { deepClone, objectMerge } from '@/utils/index';
import { isExists, isNotExists } from '@/utils/object-util'; import { isExists, isNotExists } from '@/utils/object-util';
import Layout from '@/layout';
/** /**
* Use meta.role to determine if the current user has permission * Use meta.role to determine if the current user has permission
* @param roles * @param roles
...@@ -76,6 +78,29 @@ export function filterAsyncRoutes(routesMap, routes, roles) { ...@@ -76,6 +78,29 @@ export function filterAsyncRoutes(routesMap, routes, roles) {
return resRoutes; return resRoutes;
} }
export function handleComponent(routes) {
for (let i in routes) {
let _route = routes[i];
if (_route.component && _route.component.trim().length > 0) {
if (_route.component === 'layout') {
_route.component = Layout;
} else {
/** 这里的异步不能直接在import里用template string写法,babel-eslint有bug*/
/** 两种异步写法:
* component = resolve => require([`@/views/modules/${URL}`], resolve);
* component = () => import(`@/${_route.component}`);这个也有问题
*/
//这一步多余的赋值遍历必须存在,import的bug
const name = _route.component;
_route.component = () => import(`@/${name}`);
}
}
if (_route.children && _route.children.length > 0) {
handleComponent(_route.children);
}
}
}
const state = { const state = {
routes: [], routes: [],
addRoutes: [] addRoutes: []
...@@ -93,18 +118,11 @@ const actions = { ...@@ -93,18 +118,11 @@ const actions = {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
getRoutes() getRoutes()
.then(response => { .then(response => {
let accessedRoutes, let asyncRoutes = response.data;
asyncRoutes = response.data; handleComponent(asyncRoutes);
asyncRoutes.push({ path: '*', redirect: '/404', hidden: true });
accessedRoutes = filterAsyncRoutes( commit('SET_ROUTES', asyncRoutes);
deepClone(asyncRoutesMap), resolve(asyncRoutes);
asyncRoutes,
roles
);
accessedRoutes.push({ path: '*', redirect: '/404', hidden: true });
commit('SET_ROUTES', accessedRoutes);
resolve(accessedRoutes);
}) })
.catch(error => { .catch(error => {
reject(error); reject(error);
......
@import './variables.scss';
#app { #app {
.main-container { .main-container {
min-height: 100%; min-height: 100%;
transition: margin-left .28s; transition: margin-left 0.28s;
margin-left: $sideBarWidth; margin-left: $sideBarWidth;
position: relative; position: relative;
} }
...@@ -22,7 +22,8 @@ ...@@ -22,7 +22,8 @@
// reset element-ui css // reset element-ui css
.horizontal-collapse-transition { .horizontal-collapse-transition {
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; transition: 0s width ease-in-out, 0s padding-left ease-in-out,
0s padding-right ease-in-out;
} }
.scrollbar-wrapper { .scrollbar-wrapper {
...@@ -71,11 +72,11 @@ ...@@ -71,11 +72,11 @@
} }
} }
.is-active>.el-submenu__title { .is-active > .el-submenu__title {
color: $subMenuActiveText !important; color: $subMenuActiveText !important;
} }
& .nest-menu .el-submenu>.el-submenu__title, & .nest-menu .el-submenu > .el-submenu__title,
& .el-submenu .el-menu-item { & .el-submenu .el-menu-item {
min-width: $sideBarWidth !important; min-width: $sideBarWidth !important;
background-color: $subMenuBg !important; background-color: $subMenuBg !important;
...@@ -111,7 +112,7 @@ ...@@ -111,7 +112,7 @@
.el-submenu { .el-submenu {
overflow: hidden; overflow: hidden;
&>.el-submenu__title { & > .el-submenu__title {
padding: 0 !important; padding: 0 !important;
.svg-icon { .svg-icon {
...@@ -126,8 +127,8 @@ ...@@ -126,8 +127,8 @@
.el-menu--collapse { .el-menu--collapse {
.el-submenu { .el-submenu {
&>.el-submenu__title { & > .el-submenu__title {
&>span { & > span {
height: 0; height: 0;
width: 0; width: 0;
overflow: hidden; overflow: hidden;
...@@ -150,7 +151,7 @@ ...@@ -150,7 +151,7 @@
} }
.sidebar-container { .sidebar-container {
transition: transform .28s; transition: transform 0.28s;
width: $sideBarWidth !important; width: $sideBarWidth !important;
} }
...@@ -164,7 +165,6 @@ ...@@ -164,7 +165,6 @@
} }
.withoutAnimation { .withoutAnimation {
.main-container, .main-container,
.sidebar-container { .sidebar-container {
transition: none; transition: none;
...@@ -174,13 +174,13 @@ ...@@ -174,13 +174,13 @@
// when menu collapsed // when menu collapsed
.el-menu--vertical { .el-menu--vertical {
&>.el-menu { & > .el-menu {
.svg-icon { .svg-icon {
margin-right: 16px; margin-right: 16px;
} }
} }
.nest-menu .el-submenu>.el-submenu__title, .nest-menu .el-submenu > .el-submenu__title,
.el-menu-item { .el-menu-item {
&:hover { &:hover {
// you can use $subMenuHover // you can use $subMenuHover
...@@ -189,7 +189,7 @@ ...@@ -189,7 +189,7 @@
} }
// the scroll bar appears when the subMenu is too long // the scroll bar appears when the subMenu is too long
>.el-menu--popup { > .el-menu--popup {
max-height: 100vh; max-height: 100vh;
overflow-y: auto; overflow-y: auto;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment