diff --git a/template/admin/src/components/main/components/header-search/index.vue b/template/admin/src/components/main/components/header-search/index.vue
index edc85b3de..2e43d2f20 100644
--- a/template/admin/src/components/main/components/header-search/index.vue
+++ b/template/admin/src/components/main/components/header-search/index.vue
@@ -22,6 +22,7 @@
diff --git a/template/admin/src/components/main/main.vue b/template/admin/src/components/main/main.vue
index 4379d4ef3..eb7d2d691 100644
--- a/template/admin/src/components/main/main.vue
+++ b/template/admin/src/components/main/main.vue
@@ -24,7 +24,7 @@
-->
-
+
-
+
-
+
+
-
@@ -118,9 +118,14 @@ export default {
headMenuNoShow: false,
};
},
+ watch: {
+ sider(val) {
+ console.log(val);
+ },
+ },
computed: {
- ...mapState('menus', ['childMenuList']),
...mapGetters(['errorCount']),
+ ...mapState('menu', ['sider']),
...mapState('media', ['isMobile']),
tagNavList() {
return this.$store.state.app.tagNavList;
@@ -313,4 +318,7 @@ export default {
width: 800px;
}
}
+.main-warper{
+ min-height: calc(~'100vh - 166px');
+}
diff --git a/template/admin/src/libs/system/index.js b/template/admin/src/libs/system/index.js
new file mode 100644
index 000000000..5d5c74983
--- /dev/null
+++ b/template/admin/src/libs/system/index.js
@@ -0,0 +1,224 @@
+/**
+ * 系统内置方法集,正常情况下您不应该修改或移除此文件
+ * */
+
+import { cloneDeep } from 'lodash';
+
+/**
+ * @description 根据当前路由,找打顶部菜单名称
+ * @param {String} currentPath 当前路径
+ * @param {Array} menuList 所有路径
+ * */
+function getHeaderName(to, menuList) {
+ const allMenus = [];
+ menuList.forEach((menu) => {
+ const headerName = menu.path || '';
+ const menus = transferMenu(menu, headerName);
+ allMenus.push({
+ path: menu.path,
+ header: headerName,
+ });
+ menus.forEach((item) => allMenus.push(item));
+ });
+ const currentMenu = allMenus.find((item) => {
+ if (item.path === to.path) {
+ return true;
+ } else {
+ return to.path === getPath(to, item.path);
+ }
+ });
+ return currentMenu ? currentMenu.header : null;
+}
+
+function getPath(to, path) {
+ let params = [];
+ let query = [];
+ Object.keys(to.params).forEach((item) => {
+ params.push(to.params[item]);
+ });
+ Object.keys(to.query).forEach((item) => {
+ query.push(item + '=' + to.query[item]);
+ });
+ return path + (params.length ? '/' + params.join('/') : '') + (query.length ? '?' + query.join('&') : '');
+}
+
+function transferMenu(menu, headerName) {
+ if (menu.children && menu.children.length) {
+ return menu.children.reduce((all, item) => {
+ all.push({
+ path: item.path,
+ header: headerName,
+ });
+ const foundChildren = transferMenu(item, headerName);
+ return all.concat(foundChildren);
+ }, []);
+ } else {
+ return [menu];
+ }
+}
+
+export { getHeaderName };
+
+/**
+ * @description 根据当前路由,找打顶部菜单名称
+ * @param {String} currentPath 当前路径
+ * @param {Array} menuList 所有路径
+ * */
+function getHeaderSider(menuList) {
+ return menuList.filter((item) => item.is_header === 1);
+}
+
+export { getHeaderSider };
+/**
+ * @description 根据当前路由,找以及菜单名称
+ * @param {String} currentPath 当前路径
+ * @param {Array} menuList 所有路径
+ * */
+function getOneHeaderName(menuList, path) {
+ return menuList.filter((item) => item.path === path);
+}
+
+export { getOneHeaderName };
+
+/**
+ * @description 根据当前顶栏菜单 name,找到对应的二级菜单
+ * @param {Array} menuList 所有的二级菜单
+ * @param {String} headerName 当前顶栏菜单的 name
+ * */
+function getMenuSider(menuList, headerName = '') {
+ if (headerName) {
+ return menuList.filter((item) => item.path === headerName);
+ } else {
+ return menuList;
+ }
+}
+
+export { getMenuSider };
+
+/**
+ * @description 根据当前路由,找到其所有父菜单 path,作为展开侧边栏 open-names 依据
+ * @param {String} currentPath 当前路径
+ * @param {Array} menuList 所有路径
+ * */
+// function getSiderSubmenu (currentPath, menuList) {
+// const allMenus = [];
+// menuList.forEach(menu => {
+// const menus = transferSubMenu(menu, []);
+// allMenus.push({
+// path: menu.path,
+// openNames: []
+// });
+// menus.forEach(item => allMenus.push(item));
+// });
+// const currentMenu = allMenus.find(item => item.path === currentPath);
+// return currentMenu ? currentMenu.openNames : [];
+// }
+
+function getSiderSubmenu(to, menuList) {
+ const allMenus = [];
+ menuList.forEach((menu) => {
+ const menus = transferSubMenu(menu, []);
+ allMenus.push({
+ path: menu.path,
+ openNames: [],
+ });
+ menus.forEach((item) => allMenus.push(item));
+ });
+ const currentMenu = allMenus.find((item) => {
+ if (item.openNames.length) {
+ return item.path === to.path || to.path === getPath(to, item.path);
+ }
+ });
+ return currentMenu ? currentMenu.openNames : [];
+}
+
+function transferSubMenu(menu, openNames) {
+ if (menu.children && menu.children.length) {
+ const itemOpenNames = openNames.concat([menu.path]);
+ return menu.children.reduce((all, item) => {
+ all.push({
+ path: item.path,
+ openNames: itemOpenNames,
+ });
+ const foundChildren = transferSubMenu(item, itemOpenNames);
+ return all.concat(foundChildren);
+ }, []);
+ } else {
+ return [menu].map((item) => {
+ return {
+ path: item.path,
+ openNames: openNames,
+ };
+ });
+ }
+}
+
+export { getSiderSubmenu };
+
+/**
+ * @description 递归获取所有子菜单
+ * */
+function getAllSiderMenu(menuList) {
+ let allMenus = [];
+
+ menuList.forEach((menu) => {
+ if (menu.children && menu.children.length) {
+ const menus = getMenuChildren(menu);
+ menus.forEach((item) => allMenus.push(item));
+ } else {
+ allMenus.push(menu);
+ }
+ });
+
+ return allMenus;
+}
+
+function getMenuChildren(menu) {
+ if (menu.children && menu.children.length) {
+ return menu.children.reduce((all, item) => {
+ const foundChildren = getMenuChildren(item);
+ return all.concat(foundChildren);
+ }, []);
+ } else {
+ return [menu];
+ }
+}
+
+export { getAllSiderMenu };
+
+/**
+ * @description 将菜单转为平级
+ * */
+function flattenSiderMenu(menuList, newList) {
+ menuList.forEach((menu) => {
+ let newMenu = {};
+ for (let i in menu) {
+ if (i !== 'children') newMenu[i] = cloneDeep(menu[i]);
+ }
+ newList.push(newMenu);
+ menu.children && flattenSiderMenu(menu.children, newList);
+ });
+ return newList;
+}
+
+export { flattenSiderMenu };
+
+/**
+ * @description 判断列表1中是否包含了列表2中的某一项
+ * 因为用户权限 access 为数组,includes 方法无法直接得出结论
+ * */
+function includeArray(list1, list2) {
+ let status = false;
+ if (list1 === true) {
+ return true;
+ } else {
+ if (typeof list2 !== 'object') {
+ return false;
+ }
+ list2.forEach((item) => {
+ if (list1.includes(item)) status = true;
+ });
+ return status;
+ }
+}
+export { includeArray };
diff --git a/template/admin/src/main.js b/template/admin/src/main.js
index bc3eb7fca..c6da0e682 100644
--- a/template/admin/src/main.js
+++ b/template/admin/src/main.js
@@ -55,7 +55,7 @@ import Viewer from 'v-viewer';
import VueDND from 'awe-dnd';
import formCreate from '@form-create/iview';
import modalForm from '@/utils/modalForm';
-import exportExcel from '@/utils/newToExcel.js'
+import exportExcel from '@/utils/newToExcel.js';
import videoCloud from '@/utils/videoCloud';
import { modalSure } from '@/utils/public';
import { authLapse } from '@/utils/authLapse';
@@ -67,6 +67,8 @@ import timeOptions from '@/libs/timeOptions';
import scroll from '@/libs/loading';
import * as tools from '@/libs/tools';
import VueTreeList from 'vue-tree-list';
+import { getHeaderName, getHeaderSider, getMenuSider, getSiderSubmenu } from '@/libs/system';
+
// 复制到粘贴板插件
import VueClipboard from 'vue-clipboard2';
@@ -88,9 +90,9 @@ const routerPush = Router.prototype.push;
Router.prototype.push = function push(location) {
return routerPush.call(this, location).catch((error) => error);
};
-import settings from '@/setting'
+import settings from '@/setting';
-Vue.prototype.$routeProStr = settings.routePre
+Vue.prototype.$routeProStr = settings.routePre;
// 实际打包时应该不引入mock
/* eslint-disable */
@@ -193,11 +195,45 @@ new Vue({
watch: {
// 监听路由 控制侧边栏显示 标记当前顶栏菜单(如需要)
$route(to, from) {
+ const path = to.path;
+ let menus = this.$store.state.menus.menusName;
+ console.log(menus, 'menus');
+ const menuSider = menus;
+
+ this.$store.commit('menu/setActivePath', path);
+ const openNames = getSiderSubmenu(to, menuSider);
+ this.$store.commit('menu/setOpenNames', openNames);
+ // 设置顶栏菜单 后台添加一个接口,设置顶部菜单
+ const headerSider = getHeaderSider(menuSider);
+ console.log(headerSider, 'headerSider');
+ this.$store.commit('menu/setHeader', headerSider);
+ // 指定当前侧边栏隶属顶部菜单名称。如果你没有使用顶部菜单,则设置为默认的(一般为 home)名称即可
+ const headerName = getHeaderName(to, menuSider);
+ this.$store.commit('menu/setHeaderName', headerName);
+ // 获取侧边栏菜单
+ const filterMenuSider = getMenuSider(menuSider, headerName);
+ console.log(filterMenuSider, 'filterMenuSider');
+ // 指定当前显示的侧边菜单
+ this.$store.commit('menu/setOpenMenuName', filterMenuSider[0].title);
+ this.$store.commit('menu/setSider', filterMenuSider[0]?.children || []);
if (to.meta.kefu) {
document.getElementsByTagName('body')[0].className = 'kf_mobile';
} else {
document.getElementsByTagName('body')[0].className = '';
}
+ // var storage = window.localStorage;
+ // let menus = JSON.parse(storage.getItem('menuList'));
+ // this.getMenus().then(menus => {
+ // 处理手动清除db 跳转403问题
+ if (!menus.length) {
+ if (path !== '/admin/login') {
+ this.$router.replace('/admin/login');
+ }
+ return;
+ }
+ // 在 404 时,是没有 headerName 的
+
+ // });
},
},
});
diff --git a/template/admin/src/pages/product/productAdd/index.vue b/template/admin/src/pages/product/productAdd/index.vue
index fc77833ed..d6fdfb2bd 100644
--- a/template/admin/src/pages/product/productAdd/index.vue
+++ b/template/admin/src/pages/product/productAdd/index.vue
@@ -3251,7 +3251,6 @@ export default {
font-size: 12px;
font-weight: 400;
color: #999999;
- margin-top: 14px;
}
.videbox {
diff --git a/template/admin/src/pages/setting/setSystem/index.vue b/template/admin/src/pages/setting/setSystem/index.vue
index 0649838d5..4a0056582 100644
--- a/template/admin/src/pages/setting/setSystem/index.vue
+++ b/template/admin/src/pages/setting/setSystem/index.vue
@@ -225,4 +225,7 @@ export default {
min-height: 600px;
margin-top 0px !important ;
}
+.article-manager /deep/ .ivu-form-item{
+ margin-bottom: 20px !important;
+}
diff --git a/template/admin/src/store/index.js b/template/admin/src/store/index.js
index edbc0c338..b2319ef7d 100644
--- a/template/admin/src/store/index.js
+++ b/template/admin/src/store/index.js
@@ -15,6 +15,7 @@ import VuexPersistence from 'vuex-persist';
import user from './module/user';
import app from './module/app';
import menus from './module/menus';
+import menu from './module/menu';
import userInfo from './module/userInfo';
import userLevel from './module/userLevel';
import order from './module/order';
@@ -51,6 +52,7 @@ export default new Vuex.Store({
user: state.user, //这个就是存入localStorage的值
app: state.app,
menus: state.menus,
+ menu: state.menu,
userInfo: state.userInfo,
userLevel: state.userLevel,
order: state.order,
@@ -67,6 +69,7 @@ export default new Vuex.Store({
user,
app,
menus,
+ menu,
userInfo,
userLevel,
order,
diff --git a/template/admin/src/store/module/menu.js b/template/admin/src/store/module/menu.js
new file mode 100644
index 000000000..1dc6fef4b
--- /dev/null
+++ b/template/admin/src/store/module/menu.js
@@ -0,0 +1,165 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team
+// +----------------------------------------------------------------------
+/**
+ * 菜单
+ * */
+import { cloneDeep } from 'lodash';
+import { includeArray } from '@/libs/system';
+
+// 根据 menu 配置的权限,过滤菜单
+function filterMenu(menuList, access, lastList) {
+ menuList.forEach((menu) => {
+ let menuAccess = menu.auth;
+
+ if (!menuAccess || includeArray(menuAccess, access)) {
+ let newMenu = {};
+ for (let i in menu) {
+ if (i !== 'children') newMenu[i] = cloneDeep(menu[i]);
+ }
+ if (menu.children && menu.children.length) newMenu.children = [];
+
+ lastList.push(newMenu);
+ menu.children && filterMenu(menu.children, access, newMenu.children);
+ }
+ });
+ return lastList;
+}
+// 递归处理顶部菜单问题
+function getChilden(data) {
+ if (data.children) {
+ return getChilden(data.children[0]);
+ }
+ return data.path;
+}
+
+export default {
+ namespaced: true,
+ state: {
+ // 顶部菜单
+ header: [],
+ // 一级菜单名称
+ oneMenuName: '',
+ // 侧栏菜单
+ sider: [],
+ // 当前顶栏菜单的 name
+ headerName: '',
+ // 当前所在菜单的 path
+ activePath: '',
+ // 展开的子菜单 name 集合
+ openNames: [],
+ },
+ getters: {
+ /**
+ * @description 根据 user 里登录用户权限,对侧边菜单进行鉴权过滤
+ * */
+ filterSider(state, getters, rootState) {
+ const userInfo = rootState.user.info;
+ // @权限
+ const access = userInfo.access;
+ if (access && access.length) {
+ return filterMenu(state.sider, access, []);
+ } else {
+ return filterMenu(state.sider, [], []);
+ }
+ },
+ // 处理顶部路由递归
+
+ /**
+ * @description 根据 user 里登录用户权限,对顶栏菜单进行鉴权过滤
+ * */
+ filterHeader(state, getters, rootState) {
+ // 调用递归函数
+ state.header.forEach((item) => {
+ item.path = getChilden(item);
+ });
+
+ // @权限
+ const userInfo = rootState.admin.user.info;
+ const access = userInfo.access;
+ if (access && access.length) {
+ return state.header.filter((item) => {
+ let state = true;
+ if (item.auth && !includeArray(item.auth, access)) state = false;
+ return state;
+ });
+ } else {
+ return state.header.filter((item) => {
+ let state = true;
+ if (item.auth && item.auth.length) state = false;
+ return state;
+ });
+ }
+ },
+ /**
+ * @description 当前 header 的全部信息
+ * */
+ currentHeader(state) {
+ return state.header.find((item) => item.name === state.headerName);
+ },
+ /**
+ * @description 在当前 header 下,是否隐藏 sider(及折叠按钮)
+ * */
+ hideSider(state, getters) {
+ let visible = false;
+ if (getters.currentHeader && 'hideSider' in getters.currentHeader) visible = getters.currentHeader.hideSider;
+ return visible;
+ },
+ },
+ mutations: {
+ /**
+ * @description 设置侧边栏菜单
+ * @param {Object} state vuex state
+ * @param {Array} menu menu
+ */
+ setSider(state, menu) {
+ state.sider = menu;
+ },
+ /**
+ * @description 设置侧边栏菜单
+ * @param {Object} state vuex state
+ * @param {Array} menu menu
+ */
+ setOpenMenuName(state, menu) {
+ state.oneMenuName = menu;
+ },
+ /**
+ * @description 设置顶栏菜单
+ * @param {Object} state vuex state
+ * @param {Array} menu menu
+ */
+ setHeader(state, menu) {
+ state.header = menu;
+ },
+ /**
+ * @description 设置当前顶栏菜单 name
+ * @param {Object} state vuex state
+ * @param {Array} name headerName
+ */
+ setHeaderName(state, name) {
+ state.headerName = name;
+ },
+ /**
+ * @description 设置当前所在菜单的 path,用于侧栏菜单高亮当前项
+ * @param {Object} state vuex state
+ * @param {Array} path fullPath
+ */
+ setActivePath(state, path) {
+ state.activePath = path;
+ },
+ /**
+ * @description 设置当前所在菜单的全部展开父菜单的 names 集合
+ * @param {Object} state vuex state
+ * @param {Array} names openNames
+ */
+ setOpenNames(state, names) {
+ state.openNames = names;
+ },
+ },
+};