Merge branch 'master' of https://gitee.com/y_project/RuoYi-Vue
Conflicts: ruoyi-ui/src/layout/components/Sidebar/index.vue ruoyi-ui/src/layout/components/TagsView/index.vue ruoyi/src/main/java/com/ruoyi/common/constant/Constants.java ruoyi/src/main/java/com/ruoyi/common/constant/UserConstants.java ruoyi/src/main/java/com/ruoyi/project/system/controller/SysMenuController.java ruoyi/src/main/java/com/ruoyi/project/system/service/impl/SysMenuServiceImpl.java
This commit is contained in:
commit
35e940f0b5
|
@ -8,47 +8,50 @@
|
|||
:background-color="variables.menuBg"
|
||||
:text-color="variables.menuText"
|
||||
:unique-opened="true"
|
||||
:active-text-color="variables.menuActiveText"
|
||||
:active-text-color="settings.theme"
|
||||
:collapse-transition="false"
|
||||
mode="vertical"
|
||||
>
|
||||
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
|
||||
<sidebar-item
|
||||
v-for="route in permission_routes"
|
||||
:key="route.path"
|
||||
:item="route"
|
||||
:base-path="route.path"
|
||||
/>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import Logo from './Logo'
|
||||
import SidebarItem from './SidebarItem'
|
||||
import variables from '@/assets/styles/variables.scss'
|
||||
import { mapGetters, mapState } from "vuex";
|
||||
import Logo from "./Logo";
|
||||
import SidebarItem from "./SidebarItem";
|
||||
import variables from "@/assets/styles/variables.scss";
|
||||
|
||||
export default {
|
||||
components: { SidebarItem, Logo },
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'permission_routes',
|
||||
'sidebar'
|
||||
]),
|
||||
...mapState(["settings"]),
|
||||
...mapGetters(["permission_routes", "sidebar"]),
|
||||
activeMenu() {
|
||||
const route = this.$route
|
||||
const { meta, path } = route
|
||||
const route = this.$route;
|
||||
const { meta, path } = route;
|
||||
// if set path, the sidebar will highlight the path you set
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu
|
||||
return meta.activeMenu;
|
||||
}
|
||||
return path
|
||||
return path;
|
||||
},
|
||||
showLogo() {
|
||||
return this.$store.state.settings.sidebarLogo
|
||||
return this.$store.state.settings.sidebarLogo;
|
||||
},
|
||||
variables() {
|
||||
return variables
|
||||
return variables;
|
||||
},
|
||||
isCollapse() {
|
||||
return !this.sidebar.opened
|
||||
}
|
||||
return !this.sidebar.opened;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -9,11 +9,16 @@
|
|||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||
tag="span"
|
||||
class="tags-view-item"
|
||||
:style="activeStyle(tag)"
|
||||
@click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
|
||||
@contextmenu.prevent.native="openMenu(tag,$event)"
|
||||
>
|
||||
{{ tag.title }}
|
||||
<span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
|
||||
<span
|
||||
v-if="!isAffix(tag)"
|
||||
class="el-icon-close"
|
||||
@click.prevent.stop="closeSelectedTag(tag)"
|
||||
/>
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
|
||||
|
@ -26,8 +31,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import ScrollPane from './ScrollPane'
|
||||
import path from 'path'
|
||||
import ScrollPane from "./ScrollPane";
|
||||
import path from "path";
|
||||
|
||||
export default {
|
||||
components: { ScrollPane },
|
||||
|
@ -38,160 +43,184 @@ export default {
|
|||
left: 0,
|
||||
selectedTag: {},
|
||||
affixTags: []
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
visitedViews() {
|
||||
return this.$store.state.tagsView.visitedViews
|
||||
return this.$store.state.tagsView.visitedViews;
|
||||
},
|
||||
routes() {
|
||||
return this.$store.state.permission.routes
|
||||
return this.$store.state.permission.routes;
|
||||
},
|
||||
theme() {
|
||||
return this.$store.state.settings.theme;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.addTags()
|
||||
this.moveToCurrentTag()
|
||||
this.addTags();
|
||||
this.moveToCurrentTag();
|
||||
},
|
||||
visible(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', this.closeMenu)
|
||||
document.body.addEventListener("click", this.closeMenu);
|
||||
} else {
|
||||
document.body.removeEventListener('click', this.closeMenu)
|
||||
document.body.removeEventListener("click", this.closeMenu);
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initTags()
|
||||
this.addTags()
|
||||
this.initTags();
|
||||
this.addTags();
|
||||
},
|
||||
methods: {
|
||||
isActive(route) {
|
||||
return route.path === this.$route.path
|
||||
return route.path === this.$route.path;
|
||||
},
|
||||
activeStyle(tag) {
|
||||
if (!this.isActive(tag)) return {};
|
||||
return {
|
||||
"background-color": this.theme,
|
||||
"border-color": this.theme
|
||||
};
|
||||
},
|
||||
isAffix(tag) {
|
||||
return tag.meta && tag.meta.affix
|
||||
return tag.meta && tag.meta.affix;
|
||||
},
|
||||
filterAffixTags(routes, basePath = '/') {
|
||||
let tags = []
|
||||
filterAffixTags(routes, basePath = "/") {
|
||||
let tags = [];
|
||||
routes.forEach(route => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
const tagPath = path.resolve(basePath, route.path)
|
||||
const tagPath = path.resolve(basePath, route.path);
|
||||
tags.push({
|
||||
fullPath: tagPath,
|
||||
path: tagPath,
|
||||
name: route.name,
|
||||
meta: { ...route.meta }
|
||||
})
|
||||
});
|
||||
}
|
||||
if (route.children) {
|
||||
const tempTags = this.filterAffixTags(route.children, route.path)
|
||||
const tempTags = this.filterAffixTags(
|
||||
route.children,
|
||||
route.path
|
||||
);
|
||||
if (tempTags.length >= 1) {
|
||||
tags = [...tags, ...tempTags]
|
||||
tags = [...tags, ...tempTags];
|
||||
}
|
||||
}
|
||||
})
|
||||
return tags
|
||||
});
|
||||
return tags;
|
||||
},
|
||||
initTags() {
|
||||
const affixTags = this.affixTags = this.filterAffixTags(this.routes)
|
||||
const affixTags = (this.affixTags = this.filterAffixTags(
|
||||
this.routes
|
||||
));
|
||||
for (const tag of affixTags) {
|
||||
// Must have tag name
|
||||
if (tag.name) {
|
||||
this.$store.dispatch('tagsView/addVisitedView', tag)
|
||||
this.$store.dispatch("tagsView/addVisitedView", tag);
|
||||
}
|
||||
}
|
||||
},
|
||||
addTags() {
|
||||
const { name } = this.$route
|
||||
const { name } = this.$route;
|
||||
if (name) {
|
||||
this.$store.dispatch('tagsView/addView', this.$route)
|
||||
this.$store.dispatch("tagsView/addView", this.$route);
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
},
|
||||
moveToCurrentTag() {
|
||||
const tags = this.$refs.tag
|
||||
const tags = this.$refs.tag;
|
||||
this.$nextTick(() => {
|
||||
for (const tag of tags) {
|
||||
if (tag.to.path === this.$route.path) {
|
||||
this.$refs.scrollPane.moveToTarget(tag)
|
||||
this.$refs.scrollPane.moveToTarget(tag);
|
||||
// when query is different then update
|
||||
if (tag.to.fullPath !== this.$route.fullPath) {
|
||||
this.$store.dispatch('tagsView/updateVisitedView', this.$route)
|
||||
this.$store.dispatch(
|
||||
"tagsView/updateVisitedView",
|
||||
this.$route
|
||||
);
|
||||
}
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
refreshSelectedTag(view) {
|
||||
this.$store.dispatch('tagsView/delCachedView', view).then(() => {
|
||||
const { fullPath } = view
|
||||
this.$store.dispatch("tagsView/delCachedView", view).then(() => {
|
||||
const { fullPath } = view;
|
||||
this.$nextTick(() => {
|
||||
this.$router.replace({
|
||||
path: '/redirect' + fullPath
|
||||
})
|
||||
})
|
||||
})
|
||||
path: "/redirect" + fullPath
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
closeSelectedTag(view) {
|
||||
this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
|
||||
this.$store
|
||||
.dispatch("tagsView/delView", view)
|
||||
.then(({ visitedViews }) => {
|
||||
if (this.isActive(view)) {
|
||||
this.toLastView(visitedViews, view)
|
||||
this.toLastView(visitedViews, view);
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
closeOthersTags() {
|
||||
this.$router.push(this.selectedTag)
|
||||
this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {
|
||||
this.moveToCurrentTag()
|
||||
})
|
||||
this.$router.push(this.selectedTag);
|
||||
this.$store
|
||||
.dispatch("tagsView/delOthersViews", this.selectedTag)
|
||||
.then(() => {
|
||||
this.moveToCurrentTag();
|
||||
});
|
||||
},
|
||||
closeAllTags(view) {
|
||||
this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {
|
||||
this.$store
|
||||
.dispatch("tagsView/delAllViews")
|
||||
.then(({ visitedViews }) => {
|
||||
if (this.affixTags.some(tag => tag.path === view.path)) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
this.toLastView(visitedViews, view)
|
||||
})
|
||||
this.toLastView(visitedViews, view);
|
||||
});
|
||||
},
|
||||
toLastView(visitedViews, view) {
|
||||
const latestView = visitedViews.slice(-1)[0]
|
||||
const latestView = visitedViews.slice(-1)[0];
|
||||
if (latestView) {
|
||||
this.$router.push(latestView.fullPath)
|
||||
this.$router.push(latestView.fullPath);
|
||||
} else {
|
||||
// now the default is to redirect to the home page if there is no tags-view,
|
||||
// you can adjust it according to your needs.
|
||||
if (view.name === 'Dashboard') {
|
||||
if (view.name === "Dashboard") {
|
||||
// to reload home page
|
||||
this.$router.replace({ path: '/redirect' + view.fullPath })
|
||||
this.$router.replace({ path: "/redirect" + view.fullPath });
|
||||
} else {
|
||||
this.$router.push('/')
|
||||
this.$router.push("/");
|
||||
}
|
||||
}
|
||||
},
|
||||
openMenu(tag, e) {
|
||||
const menuMinWidth = 105
|
||||
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
|
||||
const offsetWidth = this.$el.offsetWidth // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth // left boundary
|
||||
const left = e.clientX - offsetLeft + 15 // 15: margin right
|
||||
const menuMinWidth = 105;
|
||||
const offsetLeft = this.$el.getBoundingClientRect().left; // container margin left
|
||||
const offsetWidth = this.$el.offsetWidth; // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth; // left boundary
|
||||
const left = e.clientX - offsetLeft + 15; // 15: margin right
|
||||
|
||||
if (left > maxLeft) {
|
||||
this.left = maxLeft
|
||||
this.left = maxLeft;
|
||||
} else {
|
||||
this.left = left
|
||||
this.left = left;
|
||||
}
|
||||
|
||||
this.top = e.clientY
|
||||
this.visible = true
|
||||
this.selectedTag = tag
|
||||
this.top = e.clientY;
|
||||
this.visible = true;
|
||||
this.selectedTag = tag;
|
||||
},
|
||||
closeMenu() {
|
||||
this.visible = false
|
||||
}
|
||||
this.visible = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -200,7 +229,7 @@ export default {
|
|||
width: 100%;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #d8dce5;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
|
||||
.tags-view-wrapper {
|
||||
.tags-view-item {
|
||||
display: inline-block;
|
||||
|
@ -226,7 +255,7 @@ export default {
|
|||
color: #fff;
|
||||
border-color: #42b983;
|
||||
&::before {
|
||||
content: '';
|
||||
content: "";
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
|
@ -249,7 +278,7 @@ export default {
|
|||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
|
||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 7px 16px;
|
||||
|
@ -272,10 +301,10 @@ export default {
|
|||
vertical-align: 2px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
transition: all .3s cubic-bezier(.645, .045, .355, 1);
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
transform-origin: 100% 50%;
|
||||
&:before {
|
||||
transform: scale(.6);
|
||||
transform: scale(0.6);
|
||||
display: inline-block;
|
||||
vertical-align: -3px;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,16 @@ public class Constants
|
|||
*/
|
||||
public static final String GBK = "GBK";
|
||||
|
||||
/**
|
||||
* http请求
|
||||
*/
|
||||
public static final String HTTP = "http://";
|
||||
|
||||
/**
|
||||
* https请求
|
||||
*/
|
||||
public static final String HTTPS = "https://";
|
||||
|
||||
/**
|
||||
* 通用成功标识
|
||||
*/
|
||||
|
|
|
@ -36,6 +36,24 @@ public class UserConstants
|
|||
/** 是否为系统默认(是) */
|
||||
public static final String YES = "Y";
|
||||
|
||||
/** 是否菜单外链(是) */
|
||||
public static final String YES_FRAME = "0";
|
||||
|
||||
/** 是否菜单外链(否) */
|
||||
public static final String NO_FRAME = "1";
|
||||
|
||||
/** 菜单类型(目录) */
|
||||
public static final String TYPE_DIR = "M";
|
||||
|
||||
/** 菜单类型(菜单) */
|
||||
public static final String TYPE_MENU = "C";
|
||||
|
||||
/** 菜单类型(按钮) */
|
||||
public static final String TYPE_BUTTON = "F";
|
||||
|
||||
/** Layout组件标识 */
|
||||
public final static String LAYOUT = "Layout";
|
||||
|
||||
/** 校验返回结果码 */
|
||||
public final static String UNIQUE = "0";
|
||||
public final static String NOT_UNIQUE = "1";
|
||||
|
|
|
@ -12,9 +12,11 @@ import org.springframework.web.bind.annotation.PutMapping;
|
|||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.ServletUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.framework.aspectj.lang.annotation.Log;
|
||||
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
|
||||
import com.ruoyi.framework.security.LoginUser;
|
||||
|
@ -100,6 +102,11 @@ public class SysMenuController extends BaseController
|
|||
{
|
||||
return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
|
||||
}
|
||||
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame())
|
||||
&& !StringUtils.startsWithAny(menu.getPath(), Constants.HTTP, Constants.HTTPS))
|
||||
{
|
||||
return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
|
||||
}
|
||||
menu.setCreateBy(SecurityUtils.getUsername());
|
||||
return toAjax(menuService.insertMenu(menu));
|
||||
}
|
||||
|
@ -116,6 +123,11 @@ public class SysMenuController extends BaseController
|
|||
{
|
||||
return AjaxResult.error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
|
||||
}
|
||||
else if (UserConstants.YES_FRAME.equals(menu.getIsFrame())
|
||||
&& !StringUtils.startsWithAny(menu.getPath(), Constants.HTTP, Constants.HTTPS))
|
||||
{
|
||||
return AjaxResult.error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
|
||||
}
|
||||
menu.setUpdateBy(SecurityUtils.getUsername());
|
||||
return toAjax(menuService.updateMenu(menu));
|
||||
}
|
||||
|
|
|
@ -142,15 +142,26 @@ public class SysMenuServiceImpl implements ISysMenuService
|
|||
router.setHidden("1".equals(menu.getVisible()));
|
||||
router.setName(StringUtils.capitalize(menu.getPath()));
|
||||
router.setPath(getRouterPath(menu));
|
||||
router.setComponent(StringUtils.isEmpty(menu.getComponent()) ? "Layout" : menu.getComponent());
|
||||
router.setComponent(getComponent(menu));
|
||||
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
|
||||
List<SysMenu> cMenus = menu.getChildren();
|
||||
if (!cMenus.isEmpty() && cMenus.size() > 0 && "M".equals(menu.getMenuType()))
|
||||
if (!cMenus.isEmpty() && cMenus.size() > 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType()))
|
||||
{
|
||||
router.setAlwaysShow(true);
|
||||
router.setRedirect("noRedirect");
|
||||
router.setChildren(buildMenus(cMenus));
|
||||
}
|
||||
else if (isMeunFrame(menu))
|
||||
{
|
||||
List<RouterVo> childrenList = new ArrayList<RouterVo>();
|
||||
RouterVo children = new RouterVo();
|
||||
children.setPath(menu.getPath());
|
||||
children.setComponent(menu.getComponent());
|
||||
children.setName(StringUtils.capitalize(menu.getPath()));
|
||||
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
|
||||
childrenList.add(children);
|
||||
router.setChildren(childrenList);
|
||||
}
|
||||
routers.add(router);
|
||||
}
|
||||
return routers;
|
||||
|
@ -297,14 +308,48 @@ public class SysMenuServiceImpl implements ISysMenuService
|
|||
public String getRouterPath(SysMenu menu)
|
||||
{
|
||||
String routerPath = menu.getPath();
|
||||
// 非外链并且是一级目录
|
||||
if (0 == menu.getParentId() && "1".equals(menu.getIsFrame()))
|
||||
// 非外链并且是一级目录(类型为目录)
|
||||
if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType())
|
||||
&& UserConstants.NO_FRAME.equals(menu.getIsFrame()))
|
||||
{
|
||||
routerPath = "/" + menu.getPath();
|
||||
}
|
||||
// 非外链并且是一级目录(类型为菜单)
|
||||
else if (isMeunFrame(menu))
|
||||
{
|
||||
routerPath = "/";
|
||||
}
|
||||
return routerPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件信息
|
||||
*
|
||||
* @param menu 菜单信息
|
||||
* @return 组件信息
|
||||
*/
|
||||
public String getComponent(SysMenu menu)
|
||||
{
|
||||
String component = UserConstants.LAYOUT;
|
||||
if (StringUtils.isNotEmpty(menu.getComponent()) && !isMeunFrame(menu))
|
||||
{
|
||||
component = menu.getComponent();
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为菜单内部跳转
|
||||
*
|
||||
* @param menu 菜单信息
|
||||
* @return 结果
|
||||
*/
|
||||
public boolean isMeunFrame(SysMenu menu)
|
||||
{
|
||||
return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType())
|
||||
&& menu.getIsFrame().equals(UserConstants.NO_FRAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据父节点的ID获取所有子节点
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue